-
/*
This file is part of GNUnet.
Copyright (C) 2013, 2017 GNUnet e.V.
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
-
/**
* @file cadet/gnunet-service-cadet-new_tunnels.c
* @brief Information we track per tunnel.
* @author Christian Grothoff
*
* FIXME:
- * - when managing connections, distinguish those that
- * have (recently) had traffic from those that were
- * never ready (or not recently)
- * - clean up KX logic!
+ * - proper connection evaluation during connection management:
+ * + consider quality (or quality spread?) of current connection set
+ * when deciding how often to do maintenance
+ * + interact with PEER to drive DHT GET/PUT operations based
+ * on how much we like our connections
*/
#include "platform.h"
#include "gnunet_util_lib.h"
+#include "gnunet_statistics_service.h"
#include "gnunet_signatures.h"
-#include "cadet_protocol.h"
-#include "cadet_path.h"
#include "gnunet-service-cadet-new.h"
+#include "cadet_protocol.h"
#include "gnunet-service-cadet-new_channel.h"
#include "gnunet-service-cadet-new_connection.h"
#include "gnunet-service-cadet-new_tunnels.h"
#define LOG(level, ...) GNUNET_log_from(level,"cadet-tun",__VA_ARGS__)
+/**
+ * How often do we try to decrypt payload with unverified key
+ * material? Used to limit CPU increase upon receiving bogus
+ * KX.
+ */
+#define MAX_UNVERIFIED_ATTEMPTS 16
/**
* How long do we wait until tearing down an idle tunnel?
#define IDLE_DESTROY_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 90)
/**
- * Yuck, replace by 'offsetof' expression?
- * FIXME.
+ * How long do we wait initially before retransmitting the KX?
+ * TODO: replace by 2 RTT if/once we have connection-level RTT data!
*/
-#define AX_HEADER_SIZE (sizeof (uint32_t) * 2\
- + sizeof (struct GNUNET_CRYPTO_EcdhePublicKey))
-
+#define INITIAL_KX_RETRY_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 250)
/**
* Maximum number of skipped keys we keep in memory per tunnel.
struct GNUNET_CRYPTO_SymmetricSessionKey RK;
/**
- * 32-byte header key (send).
+ * 32-byte header key (currently used for sending).
*/
struct GNUNET_CRYPTO_SymmetricSessionKey HKs;
/**
- * 32-byte header key (recv)
+ * 32-byte header key (currently used for receiving)
*/
struct GNUNET_CRYPTO_SymmetricSessionKey HKr;
/**
- * 32-byte next header key (send).
+ * 32-byte next header key (for sending), used once the
+ * ratchet advances. We are sure that the sender has this
+ * key as well only after @e ratchet_allowed is #GNUNET_YES.
*/
struct GNUNET_CRYPTO_SymmetricSessionKey NHKs;
/**
- * 32-byte next header key (recv).
+ * 32-byte next header key (for receiving). To be tried
+ * when decrypting with @e HKr fails and thus the sender
+ * may have advanced the ratchet.
*/
struct GNUNET_CRYPTO_SymmetricSessionKey NHKr;
/**
- * 32-byte chain keys (used for forward-secrecy updating, send).
+ * 32-byte chain keys (used for forward-secrecy) for
+ * sending messages. Updated for every message.
*/
struct GNUNET_CRYPTO_SymmetricSessionKey CKs;
/**
- * 32-byte chain keys (used for forward-secrecy updating, recv).
+ * 32-byte chain keys (used for forward-secrecy) for
+ * receiving messages. Updated for every message. If
+ * messages are skipped, the respective derived MKs
+ * (and the current @HKr) are kept in the @e skipped_head DLL.
*/
struct GNUNET_CRYPTO_SymmetricSessionKey CKr;
/**
- * ECDH for key exchange (A0 / B0).
+ * ECDH for key exchange (A0 / B0). Note that for the
+ * 'unverified_ax', this member is an alias with the main
+ * 't->ax.kx_0' value, so do not free it!
*/
struct GNUNET_CRYPTO_EcdhePrivateKey *kx_0;
/**
- * ECDH Ratchet key (send).
+ * ECDH Ratchet key (our private key in the current DH). Note that
+ * for the 'unverified_ax', this member is an alias with the main
+ * 't->ax.kx_0' value, so do not free it!
*/
struct GNUNET_CRYPTO_EcdhePrivateKey *DHRs;
/**
- * ECDH Ratchet key (recv).
+ * ECDH Ratchet key (other peer's public key in the current DH).
*/
struct GNUNET_CRYPTO_EcdhePublicKey DHRr;
/**
- * When does this ratchet expire and a new one is triggered.
+ * Time when the current ratchet expires and a new one is triggered
+ * (if @e ratchet_allowed is #GNUNET_YES).
*/
struct GNUNET_TIME_Absolute ratchet_expiration;
int ratchet_flag;
/**
- * Number of messages recieved since our last ratchet advance.
- * - If this counter = 0, we cannot send a new ratchet key in next msg.
- * - If this counter > 0, we can (but don't yet have to) send a new key.
+ * True (#GNUNET_YES) if we have received a message from the
+ * other peer that uses the keys from our last ratchet step.
+ * This implies that we are again allowed to advance the ratchet,
+ * otherwise we have to wait until the other peer sees our current
+ * ephemeral key and advances first.
+ *
+ * #GNUNET_NO if we have advanced the ratched but lack any evidence
+ * that the other peer has noticed this.
*/
- unsigned int ratchet_allowed;
+ int ratchet_allowed;
/**
* Number of messages recieved since our last ratchet advance.
- * - If this counter = 0, we cannot send a new ratchet key in next msg.
- * - If this counter > 0, we can (but don't yet have to) send a new key.
+ *
+ * If this counter = 0, we cannot send a new ratchet key in the next
+ * message.
+ *
+ * If this counter > 0, we could (but don't have to) send a new key.
+ *
+ * Once the @e ratchet_counter is larger than
+ * #ratchet_messages (or @e ratchet_expiration time has past), and
+ * @e ratchet_allowed is #GNUNET_YES, we advance the ratchet.
*/
unsigned int ratchet_counter;
};
-/**
- * Entry in list of connections used by tunnel, with metadata.
- */
-struct CadetTConnection
-{
- /**
- * Next in DLL.
- */
- struct CadetTConnection *next;
-
- /**
- * Prev in DLL.
- */
- struct CadetTConnection *prev;
-
- /**
- * Connection handle.
- */
- struct CadetConnection *cc;
-
- /**
- * Tunnel this connection belongs to.
- */
- struct CadetTunnel *t;
-
- /**
- * Creation time, to keep oldest connection alive.
- */
- struct GNUNET_TIME_Absolute created;
-
- /**
- * Connection throughput, to keep fastest connection alive.
- */
- uint32_t throughput;
-};
-
-
/**
* Struct used to save messages in a non-ready tunnel to send once connected.
*/
/**
* Continuation to call once sent (on the channel layer).
*/
- GNUNET_SCHEDULER_TaskCallback cont;
+ GCT_SendContinuation cont;
/**
* Closure for @c cont.
struct CadetTunnelAxolotl ax;
/**
- * State of the tunnel connectivity.
+ * Unverified Axolotl info, used only if we got a fresh KX (not a
+ * KX_AUTH) while our end of the tunnel was still up. In this case,
+ * we keep the fresh KX around but do not put it into action until
+ * we got encrypted payload that assures us of the authenticity of
+ * the KX.
*/
- enum CadetTunnelCState cstate;
+ struct CadetTunnelAxolotl *unverified_ax;
/**
- * State of the tunnel encryption.
+ * Task scheduled if there are no more channels using the tunnel.
*/
- enum CadetTunnelEState estate;
+ struct GNUNET_SCHEDULER_Task *destroy_task;
+
+ /**
+ * Task to trim connections if too many are present.
+ */
+ struct GNUNET_SCHEDULER_Task *maintain_connections_task;
+
+ /**
+ * Task to send messages from queue (if possible).
+ */
+ struct GNUNET_SCHEDULER_Task *send_task;
+
+ /**
+ * Task to trigger KX.
+ */
+ struct GNUNET_SCHEDULER_Task *kx_task;
/**
- * Task to start the rekey process.
+ * Tokenizer for decrypted messages.
*/
- struct GNUNET_SCHEDULER_Task *rekey_task;
+ struct GNUNET_MessageStreamTokenizer *mst;
/**
- * DLL of connections that are actively used to reach the destination peer.
+ * Dispatcher for decrypted messages only (do NOT use for sending!).
*/
- struct CadetTConnection *connection_head;
+ struct GNUNET_MQ_Handle *mq;
/**
- * DLL of connections that are actively used to reach the destination peer.
+ * DLL of ready connections that are actively used to reach the destination peer.
*/
- struct CadetTConnection *connection_tail;
+ struct CadetTConnection *connection_ready_head;
+
+ /**
+ * DLL of ready connections that are actively used to reach the destination peer.
+ */
+ struct CadetTConnection *connection_ready_tail;
+
+ /**
+ * DLL of connections that we maintain that might be used to reach the destination peer.
+ */
+ struct CadetTConnection *connection_busy_head;
+
+ /**
+ * DLL of connections that we maintain that might be used to reach the destination peer.
+ */
+ struct CadetTConnection *connection_busy_tail;
/**
* Channels inside this tunnel. Maps
- * `struct GCT_ChannelTunnelNumber` to a `struct CadetChannel`.
+ * `struct GNUNET_CADET_ChannelTunnelNumber` to a `struct CadetChannel`.
*/
struct GNUNET_CONTAINER_MultiHashMap32 *channels;
/**
* Channel ID for the next created channel in this tunnel.
*/
- struct GCT_ChannelTunnelNumber next_chid;
+ struct GNUNET_CADET_ChannelTunnelNumber next_ctn;
/**
* Queued messages, to transmit once tunnel gets connected.
struct CadetTunnelQueueEntry *tq_tail;
/**
- * Task scheduled if there are no more channels using the tunnel.
+ * Identification of the connection from which we are currently processing
+ * a message. Only valid (non-NULL) during #handle_decrypted() and the
+ * handle-*()-functions called from our @e mq during that function.
*/
- struct GNUNET_SCHEDULER_Task *destroy_task;
+ struct CadetTConnection *current_ct;
/**
- * Task to trim connections if too many are present.
+ * How long do we wait until we retry the KX?
*/
- struct GNUNET_SCHEDULER_Task *maintain_connections_task;
+ struct GNUNET_TIME_Relative kx_retry_delay;
+
+ /**
+ * When do we try the next KX?
+ */
+ struct GNUNET_TIME_Absolute next_kx_attempt;
/**
- * Ephemeral message in the queue (to avoid queueing more than one).
+ * Number of connections in the @e connection_ready_head DLL.
*/
- struct CadetConnectionQueue *ephm_hKILL;
+ unsigned int num_ready_connections;
/**
- * Pong message in the queue.
+ * Number of connections in the @e connection_busy_head DLL.
*/
- struct CadetConnectionQueue *pong_hKILL;
+ unsigned int num_busy_connections;
/**
- * Number of connections in the @e connection_head DLL.
+ * How often have we tried and failed to decrypt a message using
+ * the unverified KX material from @e unverified_ax? Used to
+ * stop trying after #MAX_UNVERIFIED_ATTEMPTS.
*/
- unsigned int num_connections;
+ unsigned int unverified_attempts;
/**
* Number of entries in the @e tq_head DLL.
*/
unsigned int tq_len;
+
+ /**
+ * State of the tunnel encryption.
+ */
+ enum CadetTunnelEState estate;
+
+ /**
+ * Force triggering KX_AUTH independent of @e estate.
+ */
+ int kx_auth_requested;
+
};
+/**
+ * Connection @a ct is now unready, clear it's ready flag
+ * and move it from the ready DLL to the busy DLL.
+ *
+ * @param ct connection to move to unready status
+ */
+static void
+mark_connection_unready (struct CadetTConnection *ct)
+{
+ struct CadetTunnel *t = ct->t;
+
+ GNUNET_assert (GNUNET_YES == ct->is_ready);
+ GNUNET_CONTAINER_DLL_remove (t->connection_ready_head,
+ t->connection_ready_tail,
+ ct);
+ GNUNET_assert (0 < t->num_ready_connections);
+ t->num_ready_connections--;
+ ct->is_ready = GNUNET_NO;
+ GNUNET_CONTAINER_DLL_insert (t->connection_busy_head,
+ t->connection_busy_tail,
+ ct);
+ t->num_busy_connections++;
+}
+
+
/**
* Get the static string for the peer this tunnel is directed.
*
static char buf[64];
if (NULL == t)
- return "T(NULL)";
-
+ return "Tunnel(NULL)";
GNUNET_snprintf (buf,
sizeof (buf),
- "T(%s)",
- GCP_2s (t->destination));
+ "Tunnel %s",
+ GNUNET_i2s (GCP_get_id (t->destination)));
return buf;
}
+/**
+ * Get string description for tunnel encryption state.
+ *
+ * @param es Tunnel state.
+ *
+ * @return String representation.
+ */
+static const char *
+estate2s (enum CadetTunnelEState es)
+{
+ static char buf[32];
+
+ switch (es)
+ {
+ case CADET_TUNNEL_KEY_UNINITIALIZED:
+ return "CADET_TUNNEL_KEY_UNINITIALIZED";
+ case CADET_TUNNEL_KEY_AX_RECV:
+ return "CADET_TUNNEL_KEY_AX_RECV";
+ case CADET_TUNNEL_KEY_AX_SENT:
+ return "CADET_TUNNEL_KEY_AX_SENT";
+ case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
+ return "CADET_TUNNEL_KEY_AX_SENT_AND_RECV";
+ case CADET_TUNNEL_KEY_AX_AUTH_SENT:
+ return "CADET_TUNNEL_KEY_AX_AUTH_SENT";
+ case CADET_TUNNEL_KEY_OK:
+ return "CADET_TUNNEL_KEY_OK";
+ default:
+ GNUNET_snprintf (buf,
+ sizeof (buf),
+ "%u (UNKNOWN STATE)",
+ es);
+ return buf;
+ }
+}
+
+
/**
* Return the peer to which this tunnel goes.
*
}
+/**
+ * Lookup a channel by its @a ctn.
+ *
+ * @param t tunnel to look in
+ * @param ctn number of channel to find
+ * @return NULL if channel does not exist
+ */
+struct CadetChannel *
+lookup_channel (struct CadetTunnel *t,
+ struct GNUNET_CADET_ChannelTunnelNumber ctn)
+{
+ return GNUNET_CONTAINER_multihashmap32_get (t->channels,
+ ntohl (ctn.cn));
+}
+
+
/**
* Count all created connections of a tunnel. Not necessarily ready connections!
*
* @return Number of connections created, either being established or ready.
*/
unsigned int
-GCT_count_any_connections (struct CadetTunnel *t)
+GCT_count_any_connections (const struct CadetTunnel *t)
{
- return t->num_connections;
+ return t->num_ready_connections + t->num_busy_connections;
}
/**
- * Get the connectivity state of a tunnel.
- *
- * @param t Tunnel.
+ * Find first connection that is ready in the list of
+ * our connections. Picks ready connections round-robin.
*
- * @return Tunnel's connectivity state.
+ * @param t tunnel to search
+ * @return NULL if we have no connection that is ready
*/
-enum CadetTunnelCState
-GCT_get_cstate (struct CadetTunnel *t)
+static struct CadetTConnection *
+get_ready_connection (struct CadetTunnel *t)
{
- return t->cstate;
+ return t->connection_ready_head;
}
/**
- * Create a new Axolotl ephemeral (ratchet) key.
+ * Called when either we have a new connection, or a new message in the
+ * queue, or some existing connection has transmission capacity. Looks
+ * at our message queue and if there is a message, picks a connection
+ * to send it on.
*
- * @param t Tunnel.
+ * @param cls the `struct CadetTunnel` to process messages on
*/
static void
-new_ephemeral (struct CadetTunnel *t)
-{
- GNUNET_free_non_null (t->ax.DHRs);
- t->ax.DHRs = GNUNET_CRYPTO_ecdhe_key_create ();
-}
+trigger_transmissions (void *cls);
/* ************************************** start core crypto ***************************** */
+/**
+ * Create a new Axolotl ephemeral (ratchet) key.
+ *
+ * @param ax key material to update
+ */
+static void
+new_ephemeral (struct CadetTunnelAxolotl *ax)
+{
+ GNUNET_free_non_null (ax->DHRs);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Creating new ephemeral ratchet key (DHRs)\n");
+ ax->DHRs = GNUNET_CRYPTO_ecdhe_key_create ();
+}
+
+
/**
* Calculate HMAC.
*
key, sizeof (*key),
ctx, sizeof (ctx),
NULL);
- /* Two step: CADET_Hash is only 256 bits, HashCode is 512. */
+ /* Two step: GNUNET_ShortHash is only 256 bits,
+ GNUNET_HashCode is 512, so we truncate. */
GNUNET_CRYPTO_hmac (&auth_key,
plaintext,
size,
* Perform a HMAC.
*
* @param key Key to use.
- * @param hash[out] Resulting HMAC.
+ * @param[out] hash Resulting HMAC.
* @param source Source key material (data to HMAC).
* @param len Length of @a source.
*/
/**
* Encrypt data with the axolotl tunnel key.
*
- * @param t Tunnel whose key to use.
+ * @param ax key material to use.
* @param dst Destination with @a size bytes for the encrypted data.
* @param src Source of the plaintext. Can overlap with @c dst, must contain @a size bytes
* @param size Size of the buffers at @a src and @a dst
*/
static void
-t_ax_encrypt (struct CadetTunnel *t,
+t_ax_encrypt (struct CadetTunnelAxolotl *ax,
void *dst,
const void *src,
size_t size)
{
struct GNUNET_CRYPTO_SymmetricSessionKey MK;
struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
- struct CadetTunnelAxolotl *ax;
size_t out_size;
- ax = &t->ax;
ax->ratchet_counter++;
if ( (GNUNET_YES == ax->ratchet_allowed) &&
( (ratchet_messages <= ax->ratchet_counter) ||
struct GNUNET_HashCode hmac;
static const char ctx[] = "axolotl ratchet";
- new_ephemeral (t);
+ new_ephemeral (ax);
ax->HKs = ax->NHKs;
/* RK, NHKs, CKs = KDF( HMAC-HASH(RK, DH(DHRs, DHRr)) ) */
/**
* Decrypt data with the axolotl tunnel key.
*
- * @param t Tunnel whose key to use.
+ * @param ax key material to use.
* @param dst Destination for the decrypted data, must contain @a size bytes.
* @param src Source of the ciphertext. Can overlap with @c dst, must contain @a size bytes.
* @param size Size of the @a src and @a dst buffers
*/
static void
-t_ax_decrypt (struct CadetTunnel *t,
+t_ax_decrypt (struct CadetTunnelAxolotl *ax,
void *dst,
const void *src,
size_t size)
{
struct GNUNET_CRYPTO_SymmetricSessionKey MK;
struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
- struct CadetTunnelAxolotl *ax;
size_t out_size;
- ax = &t->ax;
t_hmac_derive_key (&ax->CKr,
&MK,
"0",
/**
* Encrypt header with the axolotl header key.
*
- * @param t Tunnel whose key to use.
- * @param msg Message whose header to encrypt.
+ * @param ax key material to use.
+ * @param[in|out] msg Message whose header to encrypt.
*/
static void
-t_h_encrypt (struct CadetTunnel *t,
- struct GNUNET_CADET_Encrypted *msg)
+t_h_encrypt (struct CadetTunnelAxolotl *ax,
+ struct GNUNET_CADET_TunnelEncryptedMessage *msg)
{
struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
- struct CadetTunnelAxolotl *ax;
size_t out_size;
- ax = &t->ax;
GNUNET_CRYPTO_symmetric_derive_iv (&iv,
&ax->HKs,
NULL, 0,
NULL);
- out_size = GNUNET_CRYPTO_symmetric_encrypt (&msg->Ns,
- AX_HEADER_SIZE,
+ out_size = GNUNET_CRYPTO_symmetric_encrypt (&msg->ax_header,
+ sizeof (struct GNUNET_CADET_AxHeader),
&ax->HKs,
&iv,
- &msg->Ns);
- GNUNET_assert (AX_HEADER_SIZE == out_size);
+ &msg->ax_header);
+ GNUNET_assert (sizeof (struct GNUNET_CADET_AxHeader) == out_size);
}
/**
* Decrypt header with the current axolotl header key.
*
- * @param t Tunnel whose current ax HK to use.
+ * @param ax key material to use.
* @param src Message whose header to decrypt.
* @param dst Where to decrypt header to.
*/
static void
-t_h_decrypt (struct CadetTunnel *t,
- const struct GNUNET_CADET_Encrypted *src,
- struct GNUNET_CADET_Encrypted *dst)
+t_h_decrypt (struct CadetTunnelAxolotl *ax,
+ const struct GNUNET_CADET_TunnelEncryptedMessage *src,
+ struct GNUNET_CADET_TunnelEncryptedMessage *dst)
{
struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
- struct CadetTunnelAxolotl *ax;
size_t out_size;
- ax = &t->ax;
GNUNET_CRYPTO_symmetric_derive_iv (&iv,
&ax->HKr,
NULL, 0,
NULL);
- out_size = GNUNET_CRYPTO_symmetric_decrypt (&src->Ns,
- AX_HEADER_SIZE,
+ out_size = GNUNET_CRYPTO_symmetric_decrypt (&src->ax_header.Ns,
+ sizeof (struct GNUNET_CADET_AxHeader),
&ax->HKr,
&iv,
- &dst->Ns);
- GNUNET_assert (AX_HEADER_SIZE == out_size);
+ &dst->ax_header.Ns);
+ GNUNET_assert (sizeof (struct GNUNET_CADET_AxHeader) == out_size);
}
/**
* Delete a key from the list of skipped keys.
*
- * @param t Tunnel to delete from.
+ * @param ax key material to delete @a key from.
* @param key Key to delete.
*/
static void
-delete_skipped_key (struct CadetTunnel *t,
+delete_skipped_key (struct CadetTunnelAxolotl *ax,
struct CadetTunnelSkippedKey *key)
{
- GNUNET_CONTAINER_DLL_remove (t->ax.skipped_head,
- t->ax.skipped_tail,
+ GNUNET_CONTAINER_DLL_remove (ax->skipped_head,
+ ax->skipped_tail,
key);
GNUNET_free (key);
- t->ax.skipped--;
+ ax->skipped--;
}
* Decrypt and verify data with the appropriate tunnel key and verify that the
* data has not been altered since it was sent by the remote peer.
*
- * @param t Tunnel whose key to use.
+ * @param ax key material to use.
* @param dst Destination for the plaintext.
* @param src Source of the message. Can overlap with @c dst.
* @param size Size of the message.
* @return Size of the decrypted data, -1 if an error was encountered.
*/
static ssize_t
-try_old_ax_keys (struct CadetTunnel *t,
+try_old_ax_keys (struct CadetTunnelAxolotl *ax,
void *dst,
- const struct GNUNET_CADET_Encrypted *src,
+ const struct GNUNET_CADET_TunnelEncryptedMessage *src,
size_t size)
{
struct CadetTunnelSkippedKey *key;
struct GNUNET_ShortHashCode *hmac;
struct GNUNET_CRYPTO_SymmetricInitializationVector iv;
- struct GNUNET_CADET_Encrypted plaintext_header;
+ struct GNUNET_CADET_TunnelEncryptedMessage plaintext_header;
struct GNUNET_CRYPTO_SymmetricSessionKey *valid_HK;
size_t esize;
size_t res;
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Trying skipped keys\n");
hmac = &plaintext_header.hmac;
- esize = size - sizeof (struct GNUNET_CADET_Encrypted);
+ esize = size - sizeof (struct GNUNET_CADET_TunnelEncryptedMessage);
/* Find a correct Header Key */
valid_HK = NULL;
- for (key = t->ax.skipped_head; NULL != key; key = key->next)
+ for (key = ax->skipped_head; NULL != key; key = key->next)
{
- t_hmac (&src->Ns,
- AX_HEADER_SIZE + esize,
+ t_hmac (&src->ax_header,
+ sizeof (struct GNUNET_CADET_AxHeader) + esize,
0,
&key->HK,
hmac);
return -1;
/* Should've been checked in -cadet_connection.c handle_cadet_encrypted. */
- GNUNET_assert (size > sizeof (struct GNUNET_CADET_Encrypted));
- len = size - sizeof (struct GNUNET_CADET_Encrypted);
+ GNUNET_assert (size > sizeof (struct GNUNET_CADET_TunnelEncryptedMessage));
+ len = size - sizeof (struct GNUNET_CADET_TunnelEncryptedMessage);
GNUNET_assert (len >= sizeof (struct GNUNET_MessageHeader));
/* Decrypt header */
&key->HK,
NULL, 0,
NULL);
- res = GNUNET_CRYPTO_symmetric_decrypt (&src->Ns,
- AX_HEADER_SIZE,
+ res = GNUNET_CRYPTO_symmetric_decrypt (&src->ax_header.Ns,
+ sizeof (struct GNUNET_CADET_AxHeader),
&key->HK,
&iv,
- &plaintext_header.Ns);
- GNUNET_assert (AX_HEADER_SIZE == res);
+ &plaintext_header.ax_header.Ns);
+ GNUNET_assert (sizeof (struct GNUNET_CADET_AxHeader) == res);
/* Find the correct message key */
- N = ntohl (plaintext_header.Ns);
+ N = ntohl (plaintext_header.ax_header.Ns);
while ( (NULL != key) &&
(N != key->Kn) )
key = key->next;
&key->MK,
&iv,
dst);
- delete_skipped_key (t,
+ delete_skipped_key (ax,
key);
return res;
}
/**
* Delete a key from the list of skipped keys.
*
- * @param t Tunnel to delete from.
+ * @param ax key material to delete from.
* @param HKr Header Key to use.
*/
static void
-store_skipped_key (struct CadetTunnel *t,
+store_skipped_key (struct CadetTunnelAxolotl *ax,
const struct GNUNET_CRYPTO_SymmetricSessionKey *HKr)
{
struct CadetTunnelSkippedKey *key;
key = GNUNET_new (struct CadetTunnelSkippedKey);
key->timestamp = GNUNET_TIME_absolute_get ();
- key->Kn = t->ax.Nr;
- key->HK = t->ax.HKr;
- t_hmac_derive_key (&t->ax.CKr,
+ key->Kn = ax->Nr;
+ key->HK = ax->HKr;
+ t_hmac_derive_key (&ax->CKr,
&key->MK,
"0",
1);
- t_hmac_derive_key (&t->ax.CKr,
- &t->ax.CKr,
+ t_hmac_derive_key (&ax->CKr,
+ &ax->CKr,
"1",
1);
- GNUNET_CONTAINER_DLL_insert (t->ax.skipped_head,
- t->ax.skipped_tail,
+ GNUNET_CONTAINER_DLL_insert (ax->skipped_head,
+ ax->skipped_tail,
key);
- t->ax.skipped++;
- t->ax.Nr++;
+ ax->skipped++;
+ ax->Nr++;
}
* Stage skipped AX keys and calculate the message key.
* Stores each HK and MK for skipped messages.
*
- * @param t Tunnel where to stage the keys.
+ * @param ax key material to use
* @param HKr Header key.
* @param Np Received meesage number.
* @return #GNUNET_OK if keys were stored.
- * #GNUNET_SYSERR if an error ocurred (Np not expected).
+ * #GNUNET_SYSERR if an error ocurred (@a Np not expected).
*/
static int
-store_ax_keys (struct CadetTunnel *t,
+store_ax_keys (struct CadetTunnelAxolotl *ax,
const struct GNUNET_CRYPTO_SymmetricSessionKey *HKr,
uint32_t Np)
{
int gap;
- gap = Np - t->ax.Nr;
+ gap = Np - ax->Nr;
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Storing skipped keys [%u, %u)\n",
- t->ax.Nr,
+ ax->Nr,
Np);
if (MAX_KEY_GAP < gap)
{
- /* Avoid DoS (forcing peer to do 2^33 chain HMAC operations) */
+ /* Avoid DoS (forcing peer to do more than #MAX_KEY_GAP HMAC operations) */
/* TODO: start new key exchange on return */
GNUNET_break_op (0);
LOG (GNUNET_ERROR_TYPE_WARNING,
"Got message %u, expected %u+\n",
Np,
- t->ax.Nr);
+ ax->Nr);
return GNUNET_SYSERR;
}
if (0 > gap)
return GNUNET_SYSERR;
}
- while (t->ax.Nr < Np)
- store_skipped_key (t,
+ while (ax->Nr < Np)
+ store_skipped_key (ax,
HKr);
- while (t->ax.skipped > MAX_SKIPPED_KEYS)
- delete_skipped_key (t,
- t->ax.skipped_tail);
+ while (ax->skipped > MAX_SKIPPED_KEYS)
+ delete_skipped_key (ax,
+ ax->skipped_tail);
return GNUNET_OK;
}
* Decrypt and verify data with the appropriate tunnel key and verify that the
* data has not been altered since it was sent by the remote peer.
*
- * @param t Tunnel whose key to use.
+ * @param ax key material to use
* @param dst Destination for the plaintext.
* @param src Source of the message. Can overlap with @c dst.
* @param size Size of the message.
* @return Size of the decrypted data, -1 if an error was encountered.
*/
static ssize_t
-t_ax_decrypt_and_validate (struct CadetTunnel *t,
+t_ax_decrypt_and_validate (struct CadetTunnelAxolotl *ax,
void *dst,
- const struct GNUNET_CADET_Encrypted *src,
+ const struct GNUNET_CADET_TunnelEncryptedMessage *src,
size_t size)
{
- struct CadetTunnelAxolotl *ax;
struct GNUNET_ShortHashCode msg_hmac;
struct GNUNET_HashCode hmac;
- struct GNUNET_CADET_Encrypted plaintext_header;
+ struct GNUNET_CADET_TunnelEncryptedMessage plaintext_header;
uint32_t Np;
uint32_t PNp;
size_t esize; /* Size of encryped payload */
- esize = size - sizeof (struct GNUNET_CADET_Encrypted);
- ax = &t->ax;
+ esize = size - sizeof (struct GNUNET_CADET_TunnelEncryptedMessage);
/* Try current HK */
- t_hmac (&src->Ns,
- AX_HEADER_SIZE + esize,
+ t_hmac (&src->ax_header,
+ sizeof (struct GNUNET_CADET_AxHeader) + esize,
0, &ax->HKr,
&msg_hmac);
if (0 != memcmp (&msg_hmac,
struct GNUNET_CRYPTO_EcdhePublicKey *DHRp;
/* Try Next HK */
- t_hmac (&src->Ns,
- AX_HEADER_SIZE + esize,
+ t_hmac (&src->ax_header,
+ sizeof (struct GNUNET_CADET_AxHeader) + esize,
0,
&ax->NHKr,
&msg_hmac);
sizeof (msg_hmac)))
{
/* Try the skipped keys, if that fails, we're out of luck. */
- return try_old_ax_keys (t,
+ return try_old_ax_keys (ax,
dst,
src,
size);
}
HK = ax->HKr;
ax->HKr = ax->NHKr;
- t_h_decrypt (t,
+ t_h_decrypt (ax,
src,
&plaintext_header);
- Np = ntohl (plaintext_header.Ns);
- PNp = ntohl (plaintext_header.PNs);
- DHRp = &plaintext_header.DHRs;
- store_ax_keys (t,
+ Np = ntohl (plaintext_header.ax_header.Ns);
+ PNp = ntohl (plaintext_header.ax_header.PNs);
+ DHRp = &plaintext_header.ax_header.DHRs;
+ store_ax_keys (ax,
&HK,
PNp);
}
else
{
- t_h_decrypt (t,
+ t_h_decrypt (ax,
src,
&plaintext_header);
- Np = ntohl (plaintext_header.Ns);
- PNp = ntohl (plaintext_header.PNs);
+ Np = ntohl (plaintext_header.ax_header.Ns);
+ PNp = ntohl (plaintext_header.ax_header.PNs);
}
if ( (Np != ax->Nr) &&
- (GNUNET_OK != store_ax_keys (t,
+ (GNUNET_OK != store_ax_keys (ax,
&ax->HKr,
Np)) )
{
/* Try the skipped keys, if that fails, we're out of luck. */
- return try_old_ax_keys (t,
+ return try_old_ax_keys (ax,
dst,
src,
size);
}
- t_ax_decrypt (t,
+ t_ax_decrypt (ax,
dst,
&src[1],
esize);
}
-/* ************************************** end core crypto ***************************** */
-
-
/**
- * Add a channel to a tunnel.
+ * Our tunnel became ready for the first time, notify channels
+ * that have been waiting.
*
- * @param t Tunnel.
- * @param ch Channel
- * @return unique number identifying @a ch within @a t
+ * @param cls our tunnel, not used
+ * @param key unique ID of the channel, not used
+ * @param value the `struct CadetChannel` to notify
+ * @return #GNUNET_OK (continue to iterate)
*/
-struct GCT_ChannelTunnelNumber
-GCT_add_channel (struct CadetTunnel *t,
- struct CadetChannel *ch)
+static int
+notify_tunnel_up_cb (void *cls,
+ uint32_t key,
+ void *value)
{
- struct GCT_ChannelTunnelNumber ret;
- uint32_t chid;
+ struct CadetChannel *ch = value;
- chid = ntohl (t->next_chid.channel_in_tunnel);
- while (NULL !=
- GNUNET_CONTAINER_multihashmap32_get (t->channels,
- chid))
- chid++;
- GNUNET_assert (GNUNET_YES ==
- GNUNET_CONTAINER_multihashmap32_put (t->channels,
- chid,
- ch,
- GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
- t->next_chid.channel_in_tunnel = htonl (chid + 1);
- ret.channel_in_tunnel = htonl (chid);
- return ret;
+ GCCH_tunnel_up (ch);
+ return GNUNET_OK;
}
/**
- * This tunnel is no longer used, destroy it.
+ * Change the tunnel encryption state.
+ * If the encryption state changes to OK, stop the rekey task.
*
- * @param cls the idle tunnel
+ * @param t Tunnel whose encryption state to change, or NULL.
+ * @param state New encryption state.
*/
-static void
-destroy_tunnel (void *cls)
+void
+GCT_change_estate (struct CadetTunnel *t,
+ enum CadetTunnelEState state)
{
- struct CadetTunnel *t = cls;
- struct CadetTConnection *ct;
- struct CadetTunnelQueueEntry *tqe;
+ enum CadetTunnelEState old = t->estate;
- t->destroy_task = NULL;
- GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap32_size (t->channels));
- while (NULL != (ct = t->connection_head))
- {
- GNUNET_assert (ct->t == t);
- GNUNET_CONTAINER_DLL_remove (t->connection_head,
- t->connection_tail,
- ct);
- GCC_destroy (ct->cc);
- GNUNET_free (ct);
- }
- while (NULL != (tqe = t->tq_head))
- {
- GNUNET_CONTAINER_DLL_remove (t->tq_head,
- t->tq_tail,
- tqe);
- GNUNET_MQ_discard (tqe->env);
- GNUNET_free (tqe);
- }
- GCP_drop_tunnel (t->destination,
- t);
- GNUNET_CONTAINER_multihashmap32_destroy (t->channels);
- if (NULL != t->maintain_connections_task)
+ t->estate = state;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "%s estate changed from %s to %s\n",
+ GCT_2s (t),
+ estate2s (old),
+ estate2s (state));
+
+ if ( (CADET_TUNNEL_KEY_OK != old) &&
+ (CADET_TUNNEL_KEY_OK == t->estate) )
{
- GNUNET_SCHEDULER_cancel (t->maintain_connections_task);
- t->maintain_connections_task = NULL;
+ if (NULL != t->kx_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->kx_task);
+ t->kx_task = NULL;
+ }
+ /* notify all channels that have been waiting */
+ GNUNET_CONTAINER_multihashmap32_iterate (t->channels,
+ ¬ify_tunnel_up_cb,
+ t);
+ if (NULL != t->send_task)
+ GNUNET_SCHEDULER_cancel (t->send_task);
+ t->send_task = GNUNET_SCHEDULER_add_now (&trigger_transmissions,
+ t);
}
- GNUNET_free (t);
}
/**
- * A connection is ready for transmission. Looks at our message queue
- * and if there is a message, sends it out via the connection.
+ * Send a KX message.
*
- * @param cls the `struct CadetTConnection` that is ready
+ * @param t tunnel on which to send the KX_AUTH
+ * @param ct Tunnel and connection on which to send the KX_AUTH, NULL if
+ * we are to find one that is ready.
+ * @param ax axolotl key context to use
*/
static void
-connection_ready_cb (void *cls)
+send_kx (struct CadetTunnel *t,
+ struct CadetTConnection *ct,
+ struct CadetTunnelAxolotl *ax)
{
- struct CadetTConnection *ct = cls;
- struct CadetTunnel *t = ct->t;
- struct CadetTunnelQueueEntry *tq = t->tq_head;
-
- if (NULL == tq)
- return; /* no messages pending right now */
+ struct CadetConnection *cc;
+ struct GNUNET_MQ_Envelope *env;
+ struct GNUNET_CADET_TunnelKeyExchangeMessage *msg;
+ enum GNUNET_CADET_KX_Flags flags;
- /* ready to send message 'tq' on tunnel 'ct' */
- GNUNET_assert (t == tq->t);
- GNUNET_CONTAINER_DLL_remove (t->tq_head,
- t->tq_tail,
- tq);
- if (NULL != tq->cid)
- *tq->cid = *GCC_get_id (ct->cc);
- GCC_transmit (ct->cc,
- tq->env);
- tq->cont (tq->cont_cls);
- GNUNET_free (tq);
+ if (NULL == ct)
+ ct = get_ready_connection (t);
+ if (NULL == ct)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Wanted to send %s in state %s, but no connection is ready, deferring\n",
+ GCT_2s (t),
+ estate2s (t->estate));
+ t->next_kx_attempt = GNUNET_TIME_absolute_get ();
+ return;
+ }
+ cc = ct->cc;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending KX on %s via %s using %s in state %s\n",
+ GCT_2s (t),
+ GCC_2s (cc),
+ estate2s (t->estate));
+ env = GNUNET_MQ_msg (msg,
+ GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX);
+ flags = GNUNET_CADET_KX_FLAG_FORCE_REPLY; /* always for KX */
+ msg->flags = htonl (flags);
+ msg->cid = *GCC_get_id (cc);
+ GNUNET_CRYPTO_ecdhe_key_get_public (ax->kx_0,
+ &msg->ephemeral_key);
+ GNUNET_CRYPTO_ecdhe_key_get_public (ax->DHRs,
+ &msg->ratchet_key);
+ mark_connection_unready (ct);
+ t->kx_retry_delay = GNUNET_TIME_STD_BACKOFF (t->kx_retry_delay);
+ t->next_kx_attempt = GNUNET_TIME_relative_to_absolute (t->kx_retry_delay);
+ if (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate)
+ GCT_change_estate (t,
+ CADET_TUNNEL_KEY_AX_SENT);
+ else if (CADET_TUNNEL_KEY_AX_RECV == t->estate)
+ GCT_change_estate (t,
+ CADET_TUNNEL_KEY_AX_SENT_AND_RECV);
+ GCC_transmit (cc,
+ env);
}
/**
- * Called when either we have a new connection, or a new message in the
- * queue, or some existing connection has transmission capacity. Looks
- * at our message queue and if there is a message, picks a connection
- * to send it on.
+ * Send a KX_AUTH message.
*
- * @param t tunnel to process messages on
+ * @param t tunnel on which to send the KX_AUTH
+ * @param ct Tunnel and connection on which to send the KX_AUTH, NULL if
+ * we are to find one that is ready.
+ * @param ax axolotl key context to use
+ * @param force_reply Force the other peer to reply with a KX_AUTH message
+ * (set if we would like to transmit right now, but cannot)
*/
static void
-trigger_transmissions (struct CadetTunnel *t)
+send_kx_auth (struct CadetTunnel *t,
+ struct CadetTConnection *ct,
+ struct CadetTunnelAxolotl *ax,
+ int force_reply)
{
- struct CadetTConnection *ct;
+ struct CadetConnection *cc;
+ struct GNUNET_MQ_Envelope *env;
+ struct GNUNET_CADET_TunnelKeyExchangeAuthMessage *msg;
+ enum GNUNET_CADET_KX_Flags flags;
- if (NULL == t->tq_head)
- return; /* no messages pending right now */
- for (ct = t->connection_head;
- NULL != ct;
- ct = ct->next)
- if (GNUNET_YES == GCC_is_ready (ct->cc))
- break;
+ if ( (NULL == ct) ||
+ (GNUNET_NO == ct->is_ready) )
+ ct = get_ready_connection (t);
if (NULL == ct)
- return; /* no connections ready */
- connection_ready_cb (ct);
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Wanted to send KX_AUTH on %s, but no connection is ready, deferring\n",
+ GCT_2s (t));
+ t->next_kx_attempt = GNUNET_TIME_absolute_get ();
+ t->kx_auth_requested = GNUNET_YES; /* queue KX_AUTH independent of estate */
+ return;
+ }
+ t->kx_auth_requested = GNUNET_NO; /* clear flag */
+ cc = ct->cc;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending KX_AUTH on %s using %s\n",
+ GCT_2s (t),
+ GCC_2s (ct->cc));
+
+ env = GNUNET_MQ_msg (msg,
+ GNUNET_MESSAGE_TYPE_CADET_TUNNEL_KX_AUTH);
+ flags = GNUNET_CADET_KX_FLAG_NONE;
+ if (GNUNET_YES == force_reply)
+ flags |= GNUNET_CADET_KX_FLAG_FORCE_REPLY;
+ msg->kx.flags = htonl (flags);
+ msg->kx.cid = *GCC_get_id (cc);
+ GNUNET_CRYPTO_ecdhe_key_get_public (ax->kx_0,
+ &msg->kx.ephemeral_key);
+ GNUNET_CRYPTO_ecdhe_key_get_public (ax->DHRs,
+ &msg->kx.ratchet_key);
+ /* Compute authenticator (this is the main difference to #send_kx()) */
+ GNUNET_CRYPTO_hash (&ax->RK,
+ sizeof (ax->RK),
+ &msg->auth);
+
+ /* Compute when to be triggered again; actual job will
+ be scheduled via #connection_ready_cb() */
+ t->kx_retry_delay
+ = GNUNET_TIME_STD_BACKOFF (t->kx_retry_delay);
+ t->next_kx_attempt
+ = GNUNET_TIME_relative_to_absolute (t->kx_retry_delay);
+
+ /* Send via cc, mark it as unready */
+ mark_connection_unready (ct);
+
+ /* Update state machine, unless we are already OK */
+ if (CADET_TUNNEL_KEY_OK != t->estate)
+ GCT_change_estate (t,
+ CADET_TUNNEL_KEY_AX_AUTH_SENT);
+
+ GCC_transmit (cc,
+ env);
}
/**
- * Function called to maintain the connections underlying our tunnel.
- * Tries to maintain (incl. tear down) connections for the tunnel, and
- * if there is a significant change, may trigger transmissions.
- *
- * Basically, needs to check if there are connections that perform
- * badly, and if so eventually kill them and trigger a replacement.
- * The strategy is to open one more connection than
- * #DESIRED_CONNECTIONS_PER_TUNNEL, and then periodically kick out the
- * least-performing one, and then inquire for new ones.
+ * Cleanup state used by @a ax.
*
- * @param cls the `struct CadetTunnel`
+ * @param ax state to free, but not memory of @a ax itself
*/
static void
-maintain_connections_cb (void *cls)
+cleanup_ax (struct CadetTunnelAxolotl *ax)
{
- struct CadetTunnel *t = cls;
-
- GNUNET_break (0); // FIXME: implement!
+ while (NULL != ax->skipped_head)
+ delete_skipped_key (ax,
+ ax->skipped_head);
+ GNUNET_assert (0 == ax->skipped);
+ GNUNET_free_non_null (ax->kx_0);
+ GNUNET_free_non_null (ax->DHRs);
}
/**
- * Consider using the path @a p for the tunnel @a t.
- * The tunnel destination is at offset @a off in path @a p.
+ * Update our Axolotl key state based on the KX data we received.
+ * Computes the new chain keys, and root keys, etc, and also checks
+ * wether this is a replay of the current chain.
*
- * @param cls our tunnel
- * @param path a path to our destination
- * @param off offset of the destination on path @a path
- * @return #GNUNET_YES (should keep iterating)
+ * @param[in|out] axolotl chain key state to recompute
+ * @param pid peer identity of the other peer
+ * @param ephemeral_key ephemeral public key of the other peer
+ * @param ratchet_key senders next ephemeral public key
+ * @return #GNUNET_OK on success, #GNUNET_NO if the resulting
+ * root key is already in @a ax and thus the KX is useless;
+ * #GNUNET_SYSERR on hard errors (i.e. @a pid is #my_full_id)
*/
static int
-consider_path_cb (void *cls,
- struct CadetPeerPath *path,
- unsigned int off)
+update_ax_by_kx (struct CadetTunnelAxolotl *ax,
+ const struct GNUNET_PeerIdentity *pid,
+ const struct GNUNET_CRYPTO_EcdhePublicKey *ephemeral_key,
+ const struct GNUNET_CRYPTO_EcdhePublicKey *ratchet_key)
{
- struct CadetTunnel *t = cls;
- unsigned int min_length = UINT_MAX;
- GNUNET_CONTAINER_HeapCostType max_desire = 0;
- struct CadetTConnection *ct;
+ struct GNUNET_HashCode key_material[3];
+ struct GNUNET_CRYPTO_SymmetricSessionKey keys[5];
+ const char salt[] = "CADET Axolotl salt";
+ int am_I_alice;
+
+ if (0 > GNUNET_CRYPTO_cmp_peer_identity (&my_full_id,
+ pid))
+ am_I_alice = GNUNET_YES;
+ else if (0 < GNUNET_CRYPTO_cmp_peer_identity (&my_full_id,
+ pid))
+ am_I_alice = GNUNET_NO;
+ else
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
- /* Check if we care about the new path. */
- for (ct = t->connection_head;
- NULL != ct;
- ct = ct->next)
+ if (0 == memcmp (&ax->DHRr,
+ ratchet_key,
+ sizeof (*ratchet_key)))
{
- struct CadetPeerPath *ps;
-
- ps = GCC_get_path (ct->cc);
- if (ps == path)
- return GNUNET_YES; /* duplicate */
- min_length = GNUNET_MIN (min_length,
- GCPP_get_length (ps));
- max_desire = GNUNET_MAX (max_desire,
- GCPP_get_desirability (ps));
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Ratchet key already known. Ignoring KX.\n");
+ return GNUNET_NO;
}
- /* FIXME: not sure we should really just count
- 'num_connections' here, as they may all have
+ ax->DHRr = *ratchet_key;
+
+ /* ECDH A B0 */
+ if (GNUNET_YES == am_I_alice)
+ {
+ GNUNET_CRYPTO_eddsa_ecdh (my_private_key, /* A */
+ ephemeral_key, /* B0 */
+ &key_material[0]);
+ }
+ else
+ {
+ GNUNET_CRYPTO_ecdh_eddsa (ax->kx_0, /* B0 */
+ &pid->public_key, /* A */
+ &key_material[0]);
+ }
+
+ /* ECDH A0 B */
+ if (GNUNET_YES == am_I_alice)
+ {
+ GNUNET_CRYPTO_ecdh_eddsa (ax->kx_0, /* A0 */
+ &pid->public_key, /* B */
+ &key_material[1]);
+ }
+ else
+ {
+ GNUNET_CRYPTO_eddsa_ecdh (my_private_key, /* A */
+ ephemeral_key, /* B0 */
+ &key_material[1]);
+
+
+ }
+
+ /* ECDH A0 B0 */
+ /* (This is the triple-DH, we could probably safely skip this,
+ as A0/B0 are already in the key material.) */
+ GNUNET_CRYPTO_ecc_ecdh (ax->kx_0, /* A0 or B0 */
+ ephemeral_key, /* B0 or A0 */
+ &key_material[2]);
+
+ /* KDF */
+ GNUNET_CRYPTO_kdf (keys, sizeof (keys),
+ salt, sizeof (salt),
+ &key_material, sizeof (key_material),
+ NULL);
+
+ if (0 == memcmp (&ax->RK,
+ &keys[0],
+ sizeof (ax->RK)))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Root key of handshake already known. Ignoring KX.\n");
+ return GNUNET_NO;
+ }
+
+ ax->RK = keys[0];
+ if (GNUNET_YES == am_I_alice)
+ {
+ ax->HKr = keys[1];
+ ax->NHKs = keys[2];
+ ax->NHKr = keys[3];
+ ax->CKr = keys[4];
+ ax->ratchet_flag = GNUNET_YES;
+ }
+ else
+ {
+ ax->HKs = keys[1];
+ ax->NHKr = keys[2];
+ ax->NHKs = keys[3];
+ ax->CKs = keys[4];
+ ax->ratchet_flag = GNUNET_NO;
+ ax->ratchet_expiration
+ = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get(),
+ ratchet_time);
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Try to redo the KX or KX_AUTH handshake, if we can.
+ *
+ * @param cls the `struct CadetTunnel` to do KX for.
+ */
+static void
+retry_kx (void *cls)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetTunnelAxolotl *ax;
+
+ t->kx_task = NULL;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Trying to make KX progress on %s in state %s\n",
+ GCT_2s (t),
+ estate2s (t->estate));
+ switch (t->estate)
+ {
+ case CADET_TUNNEL_KEY_UNINITIALIZED: /* first attempt */
+ case CADET_TUNNEL_KEY_AX_SENT: /* trying again */
+ send_kx (t,
+ NULL,
+ &t->ax);
+ break;
+ case CADET_TUNNEL_KEY_AX_RECV:
+ case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
+ /* We are responding, so only require reply
+ if WE have a channel waiting. */
+ if (NULL != t->unverified_ax)
+ {
+ /* Send AX_AUTH so we might get this one verified */
+ ax = t->unverified_ax;
+ }
+ else
+ {
+ /* How can this be? */
+ GNUNET_break (0);
+ ax = &t->ax;
+ }
+ send_kx_auth (t,
+ NULL,
+ ax,
+ (0 == GCT_count_channels (t))
+ ? GNUNET_NO
+ : GNUNET_YES);
+ break;
+ case CADET_TUNNEL_KEY_AX_AUTH_SENT:
+ /* We are responding, so only require reply
+ if WE have a channel waiting. */
+ if (NULL != t->unverified_ax)
+ {
+ /* Send AX_AUTH so we might get this one verified */
+ ax = t->unverified_ax;
+ }
+ else
+ {
+ /* How can this be? */
+ GNUNET_break (0);
+ ax = &t->ax;
+ }
+ send_kx_auth (t,
+ NULL,
+ ax,
+ (0 == GCT_count_channels (t))
+ ? GNUNET_NO
+ : GNUNET_YES);
+ break;
+ case CADET_TUNNEL_KEY_OK:
+ /* Must have been the *other* peer asking us to
+ respond with a KX_AUTH. */
+ if (NULL != t->unverified_ax)
+ {
+ /* Sending AX_AUTH in response to AX so we might get this one verified */
+ ax = t->unverified_ax;
+ }
+ else
+ {
+ /* Sending AX_AUTH in response to AX_AUTH */
+ ax = &t->ax;
+ }
+ send_kx_auth (t,
+ NULL,
+ ax,
+ GNUNET_NO);
+ break;
+ }
+}
+
+
+/**
+ * Handle KX message that lacks authentication (and which will thus
+ * only be considered authenticated after we respond with our own
+ * KX_AUTH and finally successfully decrypt payload).
+ *
+ * @param ct connection/tunnel combo that received encrypted message
+ * @param msg the key exchange message
+ */
+void
+GCT_handle_kx (struct CadetTConnection *ct,
+ const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg)
+{
+ struct CadetTunnel *t = ct->t;
+ struct CadetTunnelAxolotl *ax;
+ int ret;
+
+ if (0 ==
+ memcmp (&t->ax.DHRr,
+ &msg->ratchet_key,
+ sizeof (msg->ratchet_key)))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Got duplicate KX. Firing back KX_AUTH.\n");
+ send_kx_auth (t,
+ ct,
+ &t->ax,
+ GNUNET_NO);
+ return;
+ }
+
+ /* We only keep ONE unverified KX around, so if there is an existing one,
+ clean it up. */
+ if (NULL != t->unverified_ax)
+ {
+ if (0 ==
+ memcmp (&t->unverified_ax->DHRr,
+ &msg->ratchet_key,
+ sizeof (msg->ratchet_key)))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Got duplicate unverified KX on %s. Fire back KX_AUTH again.\n",
+ GCT_2s (t));
+ send_kx_auth (t,
+ ct,
+ t->unverified_ax,
+ GNUNET_NO);
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Dropping old unverified KX state. Got a fresh KX for %s.\n",
+ GCT_2s (t));
+ memset (t->unverified_ax,
+ 0,
+ sizeof (struct CadetTunnelAxolotl));
+ t->unverified_ax->DHRs = t->ax.DHRs;
+ t->unverified_ax->kx_0 = t->ax.kx_0;
+ }
+ else
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Creating fresh unverified KX for %s.\n",
+ GCT_2s (t));
+ t->unverified_ax = GNUNET_new (struct CadetTunnelAxolotl);
+ t->unverified_ax->DHRs = t->ax.DHRs;
+ t->unverified_ax->kx_0 = t->ax.kx_0;
+ }
+ /* Set as the 'current' RK/DHRr the one we are currently using,
+ so that the duplicate-detection logic of
+ #update_ax_by_kx can work. */
+ t->unverified_ax->RK = t->ax.RK;
+ t->unverified_ax->DHRr = t->ax.DHRr;
+ t->unverified_attempts = 0;
+ ax = t->unverified_ax;
+
+ /* Update 'ax' by the new key material */
+ ret = update_ax_by_kx (ax,
+ GCP_get_id (t->destination),
+ &msg->ephemeral_key,
+ &msg->ratchet_key);
+ GNUNET_break (GNUNET_SYSERR != ret);
+ if (GNUNET_OK != ret)
+ return; /* duplicate KX, nothing to do */
+
+ /* move ahead in our state machine */
+ if (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate)
+ GCT_change_estate (t,
+ CADET_TUNNEL_KEY_AX_RECV);
+ else if (CADET_TUNNEL_KEY_AX_SENT == t->estate)
+ GCT_change_estate (t,
+ CADET_TUNNEL_KEY_AX_SENT_AND_RECV);
+
+ /* KX is still not done, try again our end. */
+ if (CADET_TUNNEL_KEY_OK != t->estate)
+ {
+ if (NULL != t->kx_task)
+ GNUNET_SCHEDULER_cancel (t->kx_task);
+ t->kx_task
+ = GNUNET_SCHEDULER_add_now (&retry_kx,
+ t);
+ }
+}
+
+
+/**
+ * Handle KX_AUTH message.
+ *
+ * @param ct connection/tunnel combo that received encrypted message
+ * @param msg the key exchange message
+ */
+void
+GCT_handle_kx_auth (struct CadetTConnection *ct,
+ const struct GNUNET_CADET_TunnelKeyExchangeAuthMessage *msg)
+{
+ struct CadetTunnel *t = ct->t;
+ struct CadetTunnelAxolotl ax_tmp;
+ struct GNUNET_HashCode kx_auth;
+ int ret;
+
+ if ( (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate) ||
+ (CADET_TUNNEL_KEY_AX_RECV == t->estate) )
+ {
+ /* Confusing, we got a KX_AUTH before we even send our own
+ KX. This should not happen. We'll send our own KX ASAP anyway,
+ so let's ignore this here. */
+ GNUNET_break_op (0);
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Handling KX_AUTH message for %s\n",
+ GCT_2s (t));
+
+ /* We do everything in ax_tmp until we've checked the authentication
+ so we don't clobber anything we care about by accident. */
+ ax_tmp = t->ax;
+
+ /* Update 'ax' by the new key material */
+ ret = update_ax_by_kx (&ax_tmp,
+ GCP_get_id (t->destination),
+ &msg->kx.ephemeral_key,
+ &msg->kx.ratchet_key);
+ GNUNET_break (GNUNET_OK == ret);
+ GNUNET_CRYPTO_hash (&ax_tmp.RK,
+ sizeof (ax_tmp.RK),
+ &kx_auth);
+ if (0 != memcmp (&kx_auth,
+ &msg->auth,
+ sizeof (kx_auth)))
+ {
+ /* This KX_AUTH is not using the latest KX/KX_AUTH data
+ we transmitted to the sender, refuse it, try KX again. */
+ GNUNET_break_op (0);
+ send_kx (t,
+ NULL,
+ &t->ax);
+ return;
+ }
+ /* Yep, we're good. */
+ t->ax = ax_tmp;
+ if (NULL != t->unverified_ax)
+ {
+ /* We got some "stale" KX before, drop that. */
+ t->unverified_ax->DHRs = NULL; /* aliased with ax.DHRs */
+ t->unverified_ax->kx_0 = NULL; /* aliased with ax.DHRs */
+ cleanup_ax (t->unverified_ax);
+ GNUNET_free (t->unverified_ax);
+ t->unverified_ax = NULL;
+ }
+
+ /* move ahead in our state machine */
+ switch (t->estate)
+ {
+ case CADET_TUNNEL_KEY_UNINITIALIZED:
+ case CADET_TUNNEL_KEY_AX_RECV:
+ /* Checked above, this is impossible. */
+ GNUNET_assert (0);
+ break;
+ case CADET_TUNNEL_KEY_AX_SENT: /* This is the normal case */
+ case CADET_TUNNEL_KEY_AX_SENT_AND_RECV: /* both peers started KX */
+ case CADET_TUNNEL_KEY_AX_AUTH_SENT: /* both peers now did KX_AUTH */
+ GCT_change_estate (t,
+ CADET_TUNNEL_KEY_OK);
+ break;
+ case CADET_TUNNEL_KEY_OK:
+ /* Did not expect another KX_AUTH, but so what, still acceptable.
+ Nothing to do here. */
+ break;
+ }
+}
+
+
+
+/* ************************************** end core crypto ***************************** */
+
+
+/**
+ * Compute the next free channel tunnel number for this tunnel.
+ *
+ * @param t the tunnel
+ * @return unused number that can uniquely identify a channel in the tunnel
+ */
+static struct GNUNET_CADET_ChannelTunnelNumber
+get_next_free_ctn (struct CadetTunnel *t)
+{
+#define HIGH_BIT 0x8000000
+ struct GNUNET_CADET_ChannelTunnelNumber ret;
+ uint32_t ctn;
+ int cmp;
+ uint32_t highbit;
+
+ cmp = GNUNET_CRYPTO_cmp_peer_identity (&my_full_id,
+ GCP_get_id (GCT_get_destination (t)));
+ if (0 < cmp)
+ highbit = HIGH_BIT;
+ else if (0 > cmp)
+ highbit = 0;
+ else
+ GNUNET_assert (0); // loopback must never go here!
+ ctn = ntohl (t->next_ctn.cn);
+ while (NULL !=
+ GNUNET_CONTAINER_multihashmap32_get (t->channels,
+ ctn))
+ {
+ ctn = ((ctn + 1) & (~ HIGH_BIT)) | highbit;
+ }
+ t->next_ctn.cn = htonl (((ctn + 1) & (~ HIGH_BIT)) | highbit);
+ ret.cn = ntohl (ctn);
+ return ret;
+}
+
+
+/**
+ * Add a channel to a tunnel, and notify channel that we are ready
+ * for transmission if we are already up. Otherwise that notification
+ * will be done later in #notify_tunnel_up_cb().
+ *
+ * @param t Tunnel.
+ * @param ch Channel
+ * @return unique number identifying @a ch within @a t
+ */
+struct GNUNET_CADET_ChannelTunnelNumber
+GCT_add_channel (struct CadetTunnel *t,
+ struct CadetChannel *ch)
+{
+ struct GNUNET_CADET_ChannelTunnelNumber ctn;
+
+ ctn = get_next_free_ctn (t);
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap32_put (t->channels,
+ ntohl (ctn.cn),
+ ch,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Adding %s to %s\n",
+ GCCH_2s (ch),
+ GCT_2s (t));
+ switch (t->estate)
+ {
+ case CADET_TUNNEL_KEY_UNINITIALIZED:
+ /* waiting for connection to start KX */
+ break;
+ case CADET_TUNNEL_KEY_AX_RECV:
+ case CADET_TUNNEL_KEY_AX_SENT:
+ case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
+ /* we're currently waiting for KX to complete */
+ break;
+ case CADET_TUNNEL_KEY_AX_AUTH_SENT:
+ /* waiting for OTHER peer to send us data,
+ we might need to prompt more aggressively! */
+ if (NULL == t->kx_task)
+ t->kx_task
+ = GNUNET_SCHEDULER_add_at (t->next_kx_attempt,
+ &retry_kx,
+ t);
+ break;
+ case CADET_TUNNEL_KEY_OK:
+ /* We are ready. Tell the new channel that we are up. */
+ GCCH_tunnel_up (ch);
+ break;
+ }
+ return ctn;
+}
+
+
+/**
+ * We lost a connection, remove it from our list and clean up
+ * the connection object itself.
+ *
+ * @param ct binding of connection to tunnel of the connection that was lost.
+ */
+void
+GCT_connection_lost (struct CadetTConnection *ct)
+{
+ struct CadetTunnel *t = ct->t;
+
+ if (GNUNET_YES == ct->is_ready)
+ GNUNET_CONTAINER_DLL_remove (t->connection_ready_head,
+ t->connection_ready_tail,
+ ct);
+ else
+ GNUNET_CONTAINER_DLL_remove (t->connection_busy_head,
+ t->connection_busy_tail,
+ ct);
+ GNUNET_free (ct);
+}
+
+
+/**
+ * Clean up connection @a ct of a tunnel.
+ *
+ * @param cls the `struct CadetTunnel`
+ * @param ct connection to clean up
+ */
+static void
+destroy_t_connection (void *cls,
+ struct CadetTConnection *ct)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetConnection *cc = ct->cc;
+
+ GNUNET_assert (ct->t == t);
+ GCT_connection_lost (ct);
+ GCC_destroy_without_tunnel (cc);
+}
+
+
+/**
+ * This tunnel is no longer used, destroy it.
+ *
+ * @param cls the idle tunnel
+ */
+static void
+destroy_tunnel (void *cls)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetTunnelQueueEntry *tq;
+
+ t->destroy_task = NULL;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Destroying idle %s\n",
+ GCT_2s (t));
+ GNUNET_assert (0 == GCT_count_channels (t));
+ GCT_iterate_connections (t,
+ &destroy_t_connection,
+ t);
+ GNUNET_assert (NULL == t->connection_ready_head);
+ GNUNET_assert (NULL == t->connection_busy_head);
+ while (NULL != (tq = t->tq_head))
+ {
+ if (NULL != tq->cont)
+ tq->cont (tq->cont_cls,
+ NULL);
+ GCT_send_cancel (tq);
+ }
+ GCP_drop_tunnel (t->destination,
+ t);
+ GNUNET_CONTAINER_multihashmap32_destroy (t->channels);
+ if (NULL != t->maintain_connections_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->maintain_connections_task);
+ t->maintain_connections_task = NULL;
+ }
+ if (NULL != t->send_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->send_task);
+ t->send_task = NULL;
+ }
+ if (NULL != t->kx_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->kx_task);
+ t->kx_task = NULL;
+ }
+ GNUNET_MST_destroy (t->mst);
+ GNUNET_MQ_destroy (t->mq);
+ if (NULL != t->unverified_ax)
+ {
+ t->unverified_ax->DHRs = NULL; /* aliased with ax.DHRs */
+ t->unverified_ax->kx_0 = NULL; /* aliased with ax.DHRs */
+ cleanup_ax (t->unverified_ax);
+ GNUNET_free (t->unverified_ax);
+ }
+ cleanup_ax (&t->ax);
+ GNUNET_assert (NULL == t->destroy_task);
+ GNUNET_free (t);
+}
+
+
+/**
+ * Remove a channel from a tunnel.
+ *
+ * @param t Tunnel.
+ * @param ch Channel
+ * @param ctn unique number identifying @a ch within @a t
+ */
+void
+GCT_remove_channel (struct CadetTunnel *t,
+ struct CadetChannel *ch,
+ struct GNUNET_CADET_ChannelTunnelNumber ctn)
+{
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Removing %s from %s\n",
+ GCCH_2s (ch),
+ GCT_2s (t));
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_multihashmap32_remove (t->channels,
+ ntohl (ctn.cn),
+ ch));
+ if ( (0 ==
+ GCT_count_channels (t)) &&
+ (NULL == t->destroy_task) )
+ {
+ t->destroy_task
+ = GNUNET_SCHEDULER_add_delayed (IDLE_DESTROY_DELAY,
+ &destroy_tunnel,
+ t);
+ }
+}
+
+
+/**
+ * Destroy remaining channels during shutdown.
+ *
+ * @param cls the `struct CadetTunnel` of the channel
+ * @param key key of the channel
+ * @param value the `struct CadetChannel`
+ * @return #GNUNET_OK (continue to iterate)
+ */
+static int
+destroy_remaining_channels (void *cls,
+ uint32_t key,
+ void *value)
+{
+ struct CadetChannel *ch = value;
+
+ GCCH_handle_remote_destroy (ch,
+ NULL);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Destroys the tunnel @a t now, without delay. Used during shutdown.
+ *
+ * @param t tunnel to destroy
+ */
+void
+GCT_destroy_tunnel_now (struct CadetTunnel *t)
+{
+ GNUNET_assert (GNUNET_YES == shutting_down);
+ GNUNET_CONTAINER_multihashmap32_iterate (t->channels,
+ &destroy_remaining_channels,
+ t);
+ GNUNET_assert (0 ==
+ GCT_count_channels (t));
+ if (NULL != t->destroy_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->destroy_task);
+ t->destroy_task = NULL;
+ }
+ destroy_tunnel (t);
+}
+
+
+/**
+ * Send normal payload from queue in @a t via connection @a ct.
+ * Does nothing if our payload queue is empty.
+ *
+ * @param t tunnel to send data from
+ * @param ct connection to use for transmission (is ready)
+ */
+static void
+try_send_normal_payload (struct CadetTunnel *t,
+ struct CadetTConnection *ct)
+{
+ struct CadetTunnelQueueEntry *tq;
+
+ GNUNET_assert (GNUNET_YES == ct->is_ready);
+ tq = t->tq_head;
+ if (NULL == tq)
+ {
+ /* no messages pending right now */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Not sending payload of %s on ready %s (nothing pending)\n",
+ GCT_2s (t),
+ GCC_2s (ct->cc));
+ return;
+ }
+ /* ready to send message 'tq' on tunnel 'ct' */
+ GNUNET_assert (t == tq->t);
+ GNUNET_CONTAINER_DLL_remove (t->tq_head,
+ t->tq_tail,
+ tq);
+ if (NULL != tq->cid)
+ *tq->cid = *GCC_get_id (ct->cc);
+ mark_connection_unready (ct);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending payload of %s on %s\n",
+ GCT_2s (t),
+ GCC_2s (ct->cc));
+ GCC_transmit (ct->cc,
+ tq->env);
+ if (NULL != tq->cont)
+ tq->cont (tq->cont_cls,
+ GCC_get_id (ct->cc));
+ GNUNET_free (tq);
+}
+
+
+/**
+ * A connection is @a is_ready for transmission. Looks at our message
+ * queue and if there is a message, sends it out via the connection.
+ *
+ * @param cls the `struct CadetTConnection` that is @a is_ready
+ * @param is_ready #GNUNET_YES if connection are now ready,
+ * #GNUNET_NO if connection are no longer ready
+ */
+static void
+connection_ready_cb (void *cls,
+ int is_ready)
+{
+ struct CadetTConnection *ct = cls;
+ struct CadetTunnel *t = ct->t;
+
+ if (GNUNET_NO == is_ready)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "%s no longer ready for %s\n",
+ GCC_2s (ct->cc),
+ GCT_2s (t));
+ mark_connection_unready (ct);
+ return;
+ }
+ GNUNET_assert (GNUNET_NO == ct->is_ready);
+ GNUNET_CONTAINER_DLL_remove (t->connection_busy_head,
+ t->connection_busy_tail,
+ ct);
+ GNUNET_assert (0 < t->num_busy_connections);
+ t->num_busy_connections--;
+ ct->is_ready = GNUNET_YES;
+ GNUNET_CONTAINER_DLL_insert_tail (t->connection_ready_head,
+ t->connection_ready_tail,
+ ct);
+ t->num_ready_connections++;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "%s now ready for %s in state %s\n",
+ GCC_2s (ct->cc),
+ GCT_2s (t),
+ estate2s (t->estate));
+ switch (t->estate)
+ {
+ case CADET_TUNNEL_KEY_UNINITIALIZED:
+ /* Do not begin KX if WE have no channels waiting! */
+ if (0 == GCT_count_channels (t))
+ return;
+ /* We are uninitialized, just transmit immediately,
+ without undue delay. */
+ if (NULL != t->kx_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->kx_task);
+ t->kx_task = NULL;
+ }
+ send_kx (t,
+ ct,
+ &t->ax);
+ break;
+ case CADET_TUNNEL_KEY_AX_RECV:
+ case CADET_TUNNEL_KEY_AX_SENT:
+ case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
+ case CADET_TUNNEL_KEY_AX_AUTH_SENT:
+ /* we're currently waiting for KX to complete, schedule job */
+ if (NULL == t->kx_task)
+ t->kx_task
+ = GNUNET_SCHEDULER_add_at (t->next_kx_attempt,
+ &retry_kx,
+ t);
+ break;
+ case CADET_TUNNEL_KEY_OK:
+ if (GNUNET_YES == t->kx_auth_requested)
+ {
+ if (NULL != t->kx_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->kx_task);
+ t->kx_task = NULL;
+ }
+ send_kx_auth (t,
+ ct,
+ &t->ax,
+ GNUNET_NO);
+ return;
+ }
+ try_send_normal_payload (t,
+ ct);
+ break;
+ }
+}
+
+
+/**
+ * Called when either we have a new connection, or a new message in the
+ * queue, or some existing connection has transmission capacity. Looks
+ * at our message queue and if there is a message, picks a connection
+ * to send it on.
+ *
+ * @param cls the `struct CadetTunnel` to process messages on
+ */
+static void
+trigger_transmissions (void *cls)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetTConnection *ct;
+
+ t->send_task = NULL;
+ if (NULL == t->tq_head)
+ return; /* no messages pending right now */
+ ct = get_ready_connection (t);
+ if (NULL == ct)
+ return; /* no connections ready */
+ try_send_normal_payload (t,
+ ct);
+}
+
+
+/**
+ * Closure for #evaluate_connection. Used to assemble summary information
+ * about the existing connections so we can evaluate a new path.
+ */
+struct EvaluationSummary
+{
+
+ /**
+ * Minimum length of any of our connections, `UINT_MAX` if we have none.
+ */
+ unsigned int min_length;
+
+ /**
+ * Maximum length of any of our connections, 0 if we have none.
+ */
+ unsigned int max_length;
+
+ /**
+ * Minimum desirability of any of our connections, UINT64_MAX if we have none.
+ */
+ GNUNET_CONTAINER_HeapCostType min_desire;
+
+ /**
+ * Maximum desirability of any of our connections, 0 if we have none.
+ */
+ GNUNET_CONTAINER_HeapCostType max_desire;
+
+ /**
+ * Path we are comparing against for #evaluate_connection, can be NULL.
+ */
+ struct CadetPeerPath *path;
+
+ /**
+ * Connection deemed the "worst" so far encountered by #evaluate_connection,
+ * NULL if we did not yet encounter any connections.
+ */
+ struct CadetTConnection *worst;
+
+ /**
+ * Numeric score of @e worst, only set if @e worst is non-NULL.
+ */
+ double worst_score;
+
+ /**
+ * Set to #GNUNET_YES if we have a connection over @e path already.
+ */
+ int duplicate;
+
+};
+
+
+/**
+ * Evaluate a connection, updating our summary information in @a cls about
+ * what kinds of connections we have.
+ *
+ * @param cls the `struct EvaluationSummary *` to update
+ * @param ct a connection to include in the summary
+ */
+static void
+evaluate_connection (void *cls,
+ struct CadetTConnection *ct)
+{
+ struct EvaluationSummary *es = cls;
+ struct CadetConnection *cc = ct->cc;
+ struct CadetPeerPath *ps = GCC_get_path (cc);
+ const struct CadetConnectionMetrics *metrics;
+ GNUNET_CONTAINER_HeapCostType ct_desirability;
+ struct GNUNET_TIME_Relative uptime;
+ struct GNUNET_TIME_Relative last_use;
+ uint32_t ct_length;
+ double score;
+ double success_rate;
+
+ if (ps == es->path)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Ignoring duplicate path %s.\n",
+ GCPP_2s (es->path));
+ es->duplicate = GNUNET_YES;
+ return;
+ }
+ ct_desirability = GCPP_get_desirability (ps);
+ ct_length = GCPP_get_length (ps);
+ metrics = GCC_get_metrics (cc);
+ uptime = GNUNET_TIME_absolute_get_duration (metrics->age);
+ last_use = GNUNET_TIME_absolute_get_duration (metrics->last_use);
+ /* We add 1.0 here to avoid division by zero. */
+ success_rate = (metrics->num_acked_transmissions + 1.0) / (metrics->num_successes + 1.0);
+ score
+ = ct_desirability
+ + 100.0 / (1.0 + ct_length) /* longer paths = better */
+ + sqrt (uptime.rel_value_us / 60000000LL) /* larger uptime = better */
+ - last_use.rel_value_us / 1000L; /* longer idle = worse */
+ score *= success_rate; /* weigh overall by success rate */
+
+ if ( (NULL == es->worst) ||
+ (score < es->worst_score) )
+ {
+ es->worst = ct;
+ es->worst_score = score;
+ }
+ es->min_length = GNUNET_MIN (es->min_length,
+ ct_length);
+ es->max_length = GNUNET_MAX (es->max_length,
+ ct_length);
+ es->min_desire = GNUNET_MIN (es->min_desire,
+ ct_desirability);
+ es->max_desire = GNUNET_MAX (es->max_desire,
+ ct_desirability);
+}
+
+
+/**
+ * Consider using the path @a p for the tunnel @a t.
+ * The tunnel destination is at offset @a off in path @a p.
+ *
+ * @param cls our tunnel
+ * @param path a path to our destination
+ * @param off offset of the destination on path @a path
+ * @return #GNUNET_YES (should keep iterating)
+ */
+static int
+consider_path_cb (void *cls,
+ struct CadetPeerPath *path,
+ unsigned int off)
+{
+ struct CadetTunnel *t = cls;
+ struct EvaluationSummary es;
+ struct CadetTConnection *ct;
+
+ es.min_length = UINT_MAX;
+ es.max_length = 0;
+ es.max_desire = 0;
+ es.min_desire = UINT64_MAX;
+ es.path = path;
+ es.duplicate = GNUNET_NO;
+
+ /* Compute evaluation summary over existing connections. */
+ GCT_iterate_connections (t,
+ &evaluate_connection,
+ &es);
+ if (GNUNET_YES == es.duplicate)
+ return GNUNET_YES;
+
+ /* FIXME: not sure we should really just count
+ 'num_connections' here, as they may all have
consistently failed to connect. */
/* We iterate by increasing path length; if we have enough paths and
this one is more than twice as long than what we are currently
using, then ignore all of these super-long ones! */
- if ( (t->num_connections > DESIRED_CONNECTIONS_PER_TUNNEL) &&
- (min_length * 2 < off) )
+ if ( (GCT_count_any_connections (t) > DESIRED_CONNECTIONS_PER_TUNNEL) &&
+ (es.min_length * 2 < off) &&
+ (es.max_length < off) )
{
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Ignoring paths of length %u, they are way too long.\n",
- min_length * 2);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Ignoring paths of length %u, they are way too long.\n",
+ es.min_length * 2);
return GNUNET_NO;
}
/* If we have enough paths and this one looks no better, ignore it. */
- if ( (t->num_connections >= DESIRED_CONNECTIONS_PER_TUNNEL) &&
- (min_length < GCPP_get_length (path)) &&
- (max_desire > GCPP_get_desirability (path)) )
+ if ( (GCT_count_any_connections (t) >= DESIRED_CONNECTIONS_PER_TUNNEL) &&
+ (es.min_length < GCPP_get_length (path)) &&
+ (es.min_desire > GCPP_get_desirability (path)) &&
+ (es.max_length < off) )
{
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Ignoring path (%u/%llu) to %s, got something better already.\n",
- GCPP_get_length (path),
- (unsigned long long) GCPP_get_desirability (path),
- GCP_2s (t->destination));
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Ignoring path (%u/%llu) to %s, got something better already.\n",
+ GCPP_get_length (path),
+ (unsigned long long) GCPP_get_desirability (path),
+ GCP_2s (t->destination));
return GNUNET_YES;
}
- /* Path is interesting (better by some metric, or we don't have
- enough paths yet). */
- ct = GNUNET_new (struct CadetTConnection);
- ct->created = GNUNET_TIME_absolute_get ();
- ct->t = t;
- ct->cc = GCC_create (t->destination,
- path,
- ct,
- &connection_ready_cb,
- t);
- /* FIXME: schedule job to kill connection (and path?) if it takes
- too long to get ready! (And track performance data on how long
- other connections took with the tunnel!)
- => Note: to be done within 'connection'-logic! */
- GNUNET_CONTAINER_DLL_insert (t->connection_head,
- t->connection_tail,
- ct);
- t->num_connections++;
- return GNUNET_YES;
+ /* Path is interesting (better by some metric, or we don't have
+ enough paths yet). */
+ ct = GNUNET_new (struct CadetTConnection);
+ ct->created = GNUNET_TIME_absolute_get ();
+ ct->t = t;
+ ct->cc = GCC_create (t->destination,
+ path,
+ GNUNET_CADET_OPTION_DEFAULT, /* FIXME: set based on what channels want/need! */
+ ct,
+ &connection_ready_cb,
+ ct);
+
+ /* FIXME: schedule job to kill connection (and path?) if it takes
+ too long to get ready! (And track performance data on how long
+ other connections took with the tunnel!)
+ => Note: to be done within 'connection'-logic! */
+ GNUNET_CONTAINER_DLL_insert (t->connection_busy_head,
+ t->connection_busy_tail,
+ ct);
+ t->num_busy_connections++;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Found interesting path %s for %s, created %s\n",
+ GCPP_2s (path),
+ GCT_2s (t),
+ GCC_2s (ct->cc));
+ return GNUNET_YES;
+}
+
+
+/**
+ * Function called to maintain the connections underlying our tunnel.
+ * Tries to maintain (incl. tear down) connections for the tunnel, and
+ * if there is a significant change, may trigger transmissions.
+ *
+ * Basically, needs to check if there are connections that perform
+ * badly, and if so eventually kill them and trigger a replacement.
+ * The strategy is to open one more connection than
+ * #DESIRED_CONNECTIONS_PER_TUNNEL, and then periodically kick out the
+ * least-performing one, and then inquire for new ones.
+ *
+ * @param cls the `struct CadetTunnel`
+ */
+static void
+maintain_connections_cb (void *cls)
+{
+ struct CadetTunnel *t = cls;
+ struct GNUNET_TIME_Relative delay;
+ struct EvaluationSummary es;
+
+ t->maintain_connections_task = NULL;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Performing connection maintenance for %s.\n",
+ GCT_2s (t));
+
+ es.min_length = UINT_MAX;
+ es.max_length = 0;
+ es.max_desire = 0;
+ es.min_desire = UINT64_MAX;
+ es.path = NULL;
+ es.worst = NULL;
+ es.duplicate = GNUNET_NO;
+ GCT_iterate_connections (t,
+ &evaluate_connection,
+ &es);
+ if ( (NULL != es.worst) &&
+ (GCT_count_any_connections (t) > DESIRED_CONNECTIONS_PER_TUNNEL) )
+ {
+ /* Clear out worst-performing connection 'es.worst'. */
+ destroy_t_connection (t,
+ es.worst);
+ }
+
+ /* Consider additional paths */
+ (void) GCP_iterate_paths (t->destination,
+ &consider_path_cb,
+ t);
+
+ /* FIXME: calculate when to try again based on how well we are doing;
+ in particular, if we have to few connections, we might be able
+ to do without this (as PATHS should tell us whenever a new path
+ is available instantly; however, need to make sure this job is
+ restarted after that happens).
+ Furthermore, if the paths we do know are in a reasonably narrow
+ quality band and are plentyful, we might also consider us stabilized
+ and then reduce the frequency accordingly. */
+ delay = GNUNET_TIME_UNIT_MINUTES;
+ t->maintain_connections_task
+ = GNUNET_SCHEDULER_add_delayed (delay,
+ &maintain_connections_cb,
+ t);
}
}
+/**
+ * We got a keepalive. Track in statistics.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param msg the message we received on the tunnel
+ */
+static void
+handle_plaintext_keepalive (void *cls,
+ const struct GNUNET_MessageHeader *msg)
+{
+ struct CadetTunnel *t = cls;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received KEEPALIVE on %s\n",
+ GCT_2s (t));
+ GNUNET_STATISTICS_update (stats,
+ "# keepalives received",
+ 1,
+ GNUNET_NO);
+}
+
+
+/**
+ * Check that @a msg is well-formed.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param msg the message we received on the tunnel
+ * @return #GNUNET_OK (any variable-size payload goes)
+ */
+static int
+check_plaintext_data (void *cls,
+ const struct GNUNET_CADET_ChannelAppDataMessage *msg)
+{
+ return GNUNET_OK;
+}
+
+
+/**
+ * We received payload data for a channel. Locate the channel
+ * and process the data, or return an error if the channel is unknown.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param msg the message we received on the tunnel
+ */
+static void
+handle_plaintext_data (void *cls,
+ const struct GNUNET_CADET_ChannelAppDataMessage *msg)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetChannel *ch;
+
+ ch = lookup_channel (t,
+ msg->ctn);
+ if (NULL == ch)
+ {
+ /* We don't know about such a channel, might have been destroyed on our
+ end in the meantime, or never existed. Send back a DESTROY. */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Receicved %u bytes of application data for unknown channel %u, sending DESTROY\n",
+ (unsigned int) (ntohs (msg->header.size) - sizeof (*msg)),
+ ntohl (msg->ctn.cn));
+ GCT_send_channel_destroy (t,
+ msg->ctn);
+ return;
+ }
+ GCCH_handle_channel_plaintext_data (ch,
+ GCC_get_id (t->current_ct->cc),
+ msg);
+}
+
+
+/**
+ * We received an acknowledgement for data we sent on a channel.
+ * Locate the channel and process it, or return an error if the
+ * channel is unknown.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param ack the message we received on the tunnel
+ */
+static void
+handle_plaintext_data_ack (void *cls,
+ const struct GNUNET_CADET_ChannelDataAckMessage *ack)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetChannel *ch;
+
+ ch = lookup_channel (t,
+ ack->ctn);
+ if (NULL == ch)
+ {
+ /* We don't know about such a channel, might have been destroyed on our
+ end in the meantime, or never existed. Send back a DESTROY. */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Receicved DATA_ACK for unknown channel %u, sending DESTROY\n",
+ ntohl (ack->ctn.cn));
+ GCT_send_channel_destroy (t,
+ ack->ctn);
+ return;
+ }
+ GCCH_handle_channel_plaintext_data_ack (ch,
+ GCC_get_id (t->current_ct->cc),
+ ack);
+}
+
+
+/**
+ * We have received a request to open a channel to a port from
+ * another peer. Creates the incoming channel.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param copen the message we received on the tunnel
+ */
+static void
+handle_plaintext_channel_open (void *cls,
+ const struct GNUNET_CADET_ChannelOpenMessage *copen)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetChannel *ch;
+
+ ch = GNUNET_CONTAINER_multihashmap32_get (t->channels,
+ ntohl (copen->ctn.cn));
+ if (NULL != ch)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Receicved duplicate channel OPEN on port %s from %s (%s), resending ACK\n",
+ GNUNET_h2s (&copen->port),
+ GCT_2s (t),
+ GCCH_2s (ch));
+ GCCH_handle_duplicate_open (ch,
+ GCC_get_id (t->current_ct->cc));
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Receicved channel OPEN on port %s from %s\n",
+ GNUNET_h2s (&copen->port),
+ GCT_2s (t));
+ ch = GCCH_channel_incoming_new (t,
+ copen->ctn,
+ &copen->port,
+ ntohl (copen->opt));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap32_put (t->channels,
+ ntohl (copen->ctn.cn),
+ ch,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+}
+
+
+/**
+ * Send a DESTROY message via the tunnel.
+ *
+ * @param t the tunnel to transmit over
+ * @param ctn ID of the channel to destroy
+ */
+void
+GCT_send_channel_destroy (struct CadetTunnel *t,
+ struct GNUNET_CADET_ChannelTunnelNumber ctn)
+{
+ struct GNUNET_CADET_ChannelManageMessage msg;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Sending DESTORY message for channel ID %u\n",
+ ntohl (ctn.cn));
+ msg.header.size = htons (sizeof (msg));
+ msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_CHANNEL_DESTROY);
+ msg.reserved = htonl (0);
+ msg.ctn = ctn;
+ GCT_send (t,
+ &msg.header,
+ NULL,
+ NULL);
+}
+
+
+/**
+ * We have received confirmation from the target peer that the
+ * given channel could be established (the port is open).
+ * Tell the client.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param cm the message we received on the tunnel
+ */
+static void
+handle_plaintext_channel_open_ack (void *cls,
+ const struct GNUNET_CADET_ChannelManageMessage *cm)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetChannel *ch;
+
+ ch = lookup_channel (t,
+ cm->ctn);
+ if (NULL == ch)
+ {
+ /* We don't know about such a channel, might have been destroyed on our
+ end in the meantime, or never existed. Send back a DESTROY. */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received channel OPEN_ACK for unknown channel %u, sending DESTROY\n",
+ ntohl (cm->ctn.cn));
+ GCT_send_channel_destroy (t,
+ cm->ctn);
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received channel OPEN_ACK on channel %s from %s\n",
+ GCCH_2s (ch),
+ GCT_2s (t));
+ GCCH_handle_channel_open_ack (ch,
+ GCC_get_id (t->current_ct->cc));
+}
+
+
+/**
+ * We received a message saying that a channel should be destroyed.
+ * Pass it on to the correct channel.
+ *
+ * @param cls the `struct CadetTunnel` for which we decrypted the message
+ * @param cm the message we received on the tunnel
+ */
+static void
+handle_plaintext_channel_destroy (void *cls,
+ const struct GNUNET_CADET_ChannelManageMessage *cm)
+{
+ struct CadetTunnel *t = cls;
+ struct CadetChannel *ch;
+
+ ch = lookup_channel (t,
+ cm->ctn);
+ if (NULL == ch)
+ {
+ /* We don't know about such a channel, might have been destroyed on our
+ end in the meantime, or never existed. */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received channel DESTORY for unknown channel %u. Ignoring.\n",
+ ntohl (cm->ctn.cn));
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Receicved channel DESTROY on %s from %s\n",
+ GCCH_2s (ch),
+ GCT_2s (t));
+ GCCH_handle_remote_destroy (ch,
+ GCC_get_id (t->current_ct->cc));
+}
+
+
+/**
+ * Handles a message we decrypted, by injecting it into
+ * our message queue (which will do the dispatching).
+ *
+ * @param cls the `struct CadetTunnel` that got the message
+ * @param msg the message
+ * @return #GNUNET_OK (continue to process)
+ */
+static int
+handle_decrypted (void *cls,
+ const struct GNUNET_MessageHeader *msg)
+{
+ struct CadetTunnel *t = cls;
+
+ GNUNET_assert (NULL != t->current_ct);
+ GNUNET_MQ_inject_message (t->mq,
+ msg);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Function called if we had an error processing
+ * an incoming decrypted message.
+ *
+ * @param cls the `struct CadetTunnel`
+ * @param error error code
+ */
+static void
+decrypted_error_cb (void *cls,
+ enum GNUNET_MQ_Error error)
+{
+ GNUNET_break_op (0);
+}
+
+
/**
* Create a tunnel to @a destionation. Must only be called
* from within #GCP_get_tunnel().
struct CadetTunnel *
GCT_create_tunnel (struct CadetPeer *destination)
{
- struct CadetTunnel *t;
-
- t = GNUNET_new (struct CadetTunnel);
+ struct CadetTunnel *t = GNUNET_new (struct CadetTunnel);
+ struct GNUNET_MQ_MessageHandler handlers[] = {
+ GNUNET_MQ_hd_fixed_size (plaintext_keepalive,
+ GNUNET_MESSAGE_TYPE_CADET_CHANNEL_KEEPALIVE,
+ struct GNUNET_MessageHeader,
+ t),
+ GNUNET_MQ_hd_var_size (plaintext_data,
+ GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA,
+ struct GNUNET_CADET_ChannelAppDataMessage,
+ t),
+ GNUNET_MQ_hd_fixed_size (plaintext_data_ack,
+ GNUNET_MESSAGE_TYPE_CADET_CHANNEL_APP_DATA_ACK,
+ struct GNUNET_CADET_ChannelDataAckMessage,
+ t),
+ GNUNET_MQ_hd_fixed_size (plaintext_channel_open,
+ GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN,
+ struct GNUNET_CADET_ChannelOpenMessage,
+ t),
+ GNUNET_MQ_hd_fixed_size (plaintext_channel_open_ack,
+ GNUNET_MESSAGE_TYPE_CADET_CHANNEL_OPEN_ACK,
+ struct GNUNET_CADET_ChannelManageMessage,
+ t),
+ GNUNET_MQ_hd_fixed_size (plaintext_channel_destroy,
+ GNUNET_MESSAGE_TYPE_CADET_CHANNEL_DESTROY,
+ struct GNUNET_CADET_ChannelManageMessage,
+ t),
+ GNUNET_MQ_handler_end ()
+ };
+
+ t->kx_retry_delay = INITIAL_KX_RETRY_DELAY;
+ new_ephemeral (&t->ax);
+ t->ax.kx_0 = GNUNET_CRYPTO_ecdhe_key_create ();
t->destination = destination;
t->channels = GNUNET_CONTAINER_multihashmap32_create (8);
- (void) GCP_iterate_paths (destination,
- &consider_path_cb,
- t);
t->maintain_connections_task
= GNUNET_SCHEDULER_add_now (&maintain_connections_cb,
t);
+ t->mq = GNUNET_MQ_queue_for_callbacks (NULL,
+ NULL,
+ NULL,
+ NULL,
+ handlers,
+ &decrypted_error_cb,
+ t);
+ t->mst = GNUNET_MST_create (&handle_decrypted,
+ t);
return t;
}
/**
- * Remove a channel from a tunnel.
+ * Add a @a connection to the @a tunnel.
*
- * @param t Tunnel.
- * @param ch Channel
- * @param gid unique number identifying @a ch within @a t
+ * @param t a tunnel
+ * @param cid connection identifer to use for the connection
+ * @param options options for the connection
+ * @param path path to use for the connection
+ * @return #GNUNET_OK on success,
+ * #GNUNET_SYSERR on failure (duplicate connection)
+ */
+int
+GCT_add_inbound_connection (struct CadetTunnel *t,
+ const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
+ enum GNUNET_CADET_ChannelOption options,
+ struct CadetPeerPath *path)
+{
+ struct CadetTConnection *ct;
+
+ ct = GNUNET_new (struct CadetTConnection);
+ ct->created = GNUNET_TIME_absolute_get ();
+ ct->t = t;
+ ct->cc = GCC_create_inbound (t->destination,
+ path,
+ options,
+ ct,
+ cid,
+ &connection_ready_cb,
+ ct);
+ if (NULL == ct->cc)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "%s refused inbound %s (duplicate)\n",
+ GCT_2s (t),
+ GCC_2s (ct->cc));
+ GNUNET_free (ct);
+ return GNUNET_SYSERR;
+ }
+ /* FIXME: schedule job to kill connection (and path?) if it takes
+ too long to get ready! (And track performance data on how long
+ other connections took with the tunnel!)
+ => Note: to be done within 'connection'-logic! */
+ GNUNET_CONTAINER_DLL_insert (t->connection_busy_head,
+ t->connection_busy_tail,
+ ct);
+ t->num_busy_connections++;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "%s has new %s\n",
+ GCT_2s (t),
+ GCC_2s (ct->cc));
+ return GNUNET_OK;
+}
+
+
+/**
+ * Handle encrypted message.
+ *
+ * @param ct connection/tunnel combo that received encrypted message
+ * @param msg the encrypted message to decrypt
*/
void
-GCT_remove_channel (struct CadetTunnel *t,
- struct CadetChannel *ch,
- struct GCT_ChannelTunnelNumber gid)
+GCT_handle_encrypted (struct CadetTConnection *ct,
+ const struct GNUNET_CADET_TunnelEncryptedMessage *msg)
{
- GNUNET_assert (GNUNET_YES ==
- GNUNET_CONTAINER_multihashmap32_remove (t->channels,
- ntohl (gid.channel_in_tunnel),
- ch));
- if (0 ==
- GNUNET_CONTAINER_multihashmap32_size (t->channels))
+ struct CadetTunnel *t = ct->t;
+ uint16_t size = ntohs (msg->header.size);
+ char cbuf [size] GNUNET_ALIGN;
+ ssize_t decrypted_size;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "%s received %u bytes of encrypted data in state %d\n",
+ GCT_2s (t),
+ (unsigned int) size,
+ t->estate);
+
+ switch (t->estate)
+ {
+ case CADET_TUNNEL_KEY_UNINITIALIZED:
+ case CADET_TUNNEL_KEY_AX_RECV:
+ /* We did not even SEND our KX, how can the other peer
+ send us encrypted data? */
+ GNUNET_break_op (0);
+ return;
+ case CADET_TUNNEL_KEY_AX_SENT_AND_RECV:
+ /* We send KX, and other peer send KX to us at the same time.
+ Neither KX is AUTH'ed, so let's try KX_AUTH this time. */
+ GNUNET_STATISTICS_update (stats,
+ "# received encrypted without KX_AUTH",
+ 1,
+ GNUNET_NO);
+ if (NULL != t->kx_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->kx_task);
+ t->kx_task = NULL;
+ }
+ send_kx_auth (t,
+ ct,
+ &t->ax,
+ GNUNET_YES);
+ return;
+ case CADET_TUNNEL_KEY_AX_SENT:
+ /* We did not get the KX of the other peer, but that
+ might have been lost. Send our KX again immediately. */
+ GNUNET_STATISTICS_update (stats,
+ "# received encrypted without KX",
+ 1,
+ GNUNET_NO);
+ if (NULL != t->kx_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->kx_task);
+ t->kx_task = NULL;
+ }
+ send_kx (t,
+ ct,
+ &t->ax);
+ return;
+ case CADET_TUNNEL_KEY_AX_AUTH_SENT:
+ /* Great, first payload, we might graduate to OK! */
+ case CADET_TUNNEL_KEY_OK:
+ /* We are up and running, all good. */
+ break;
+ }
+
+ GNUNET_STATISTICS_update (stats,
+ "# received encrypted",
+ 1,
+ GNUNET_NO);
+ decrypted_size = -1;
+ if (CADET_TUNNEL_KEY_OK == t->estate)
+ {
+ /* We have well-established key material available,
+ try that. (This is the common case.) */
+ decrypted_size = t_ax_decrypt_and_validate (&t->ax,
+ cbuf,
+ msg,
+ size);
+ }
+
+ if ( (-1 == decrypted_size) &&
+ (NULL != t->unverified_ax) )
+ {
+ /* We have un-authenticated KX material available. We should try
+ this as a back-up option, in case the sender crashed and
+ switched keys. */
+ decrypted_size = t_ax_decrypt_and_validate (t->unverified_ax,
+ cbuf,
+ msg,
+ size);
+ if (-1 != decrypted_size)
+ {
+ /* It worked! Treat this as authentication of the AX data! */
+ t->ax.DHRs = NULL; /* aliased with ax.DHRs */
+ t->ax.kx_0 = NULL; /* aliased with ax.DHRs */
+ cleanup_ax (&t->ax);
+ t->ax = *t->unverified_ax;
+ GNUNET_free (t->unverified_ax);
+ t->unverified_ax = NULL;
+ }
+ if (CADET_TUNNEL_KEY_AX_AUTH_SENT == t->estate)
+ {
+ /* First time it worked, move tunnel into production! */
+ GCT_change_estate (t,
+ CADET_TUNNEL_KEY_OK);
+ if (NULL != t->send_task)
+ GNUNET_SCHEDULER_cancel (t->send_task);
+ t->send_task = GNUNET_SCHEDULER_add_now (&trigger_transmissions,
+ t);
+ }
+ }
+ if (NULL != t->unverified_ax)
{
- t->destroy_task = GNUNET_SCHEDULER_add_delayed (IDLE_DESTROY_DELAY,
- &destroy_tunnel,
- t);
+ /* We had unverified KX material that was useless; so increment
+ counter and eventually move to ignore it. Note that we even do
+ this increment if we successfully decrypted with the old KX
+ material and thus didn't even both with the new one. This is
+ the ideal case, as a malicious injection of bogus KX data
+ basically only causes us to increment a counter a few times. */
+ t->unverified_attempts++;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Failed to decrypt message with unverified KX data %u times\n",
+ t->unverified_attempts);
+ if (t->unverified_attempts > MAX_UNVERIFIED_ATTEMPTS)
+ {
+ t->unverified_ax->DHRs = NULL; /* aliased with ax.DHRs */
+ t->unverified_ax->kx_0 = NULL; /* aliased with ax.DHRs */
+ cleanup_ax (t->unverified_ax);
+ GNUNET_free (t->unverified_ax);
+ t->unverified_ax = NULL;
+ }
+ }
+
+ if (-1 == decrypted_size)
+ {
+ /* Decryption failed for good, complain. */
+ GNUNET_break_op (0);
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ "%s failed to decrypt and validate encrypted data\n",
+ GCT_2s (t));
+ GNUNET_STATISTICS_update (stats,
+ "# unable to decrypt",
+ 1,
+ GNUNET_NO);
+ return;
}
+
+ /* The MST will ultimately call #handle_decrypted() on each message. */
+ t->current_ct = ct;
+ GNUNET_break_op (GNUNET_OK ==
+ GNUNET_MST_from_buffer (t->mst,
+ cbuf,
+ decrypted_size,
+ GNUNET_YES,
+ GNUNET_NO));
+ t->current_ct = NULL;
}
* @param t Tunnel on which this message is transmitted.
* @param cont Continuation to call once message is really sent.
* @param cont_cls Closure for @c cont.
- * @return Handle to cancel message. NULL if @c cont is NULL.
+ * @return Handle to cancel message
*/
struct CadetTunnelQueueEntry *
GCT_send (struct CadetTunnel *t,
const struct GNUNET_MessageHeader *message,
- GNUNET_SCHEDULER_TaskCallback cont,
+ GCT_SendContinuation cont,
void *cont_cls)
{
struct CadetTunnelQueueEntry *tq;
uint16_t payload_size;
struct GNUNET_MQ_Envelope *env;
- struct GNUNET_CADET_Encrypted *ax_msg;
-
- /* FIXME: what about KX not yet being ready? (see "is_ready()" check in old code!) */
+ struct GNUNET_CADET_TunnelEncryptedMessage *ax_msg;
+ if (CADET_TUNNEL_KEY_OK != t->estate)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
payload_size = ntohs (message->size);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Encrypting %u bytes for %s\n",
+ (unsigned int) payload_size,
+ GCT_2s (t));
env = GNUNET_MQ_msg_extra (ax_msg,
payload_size,
- GNUNET_MESSAGE_TYPE_CADET_ENCRYPTED);
- t_ax_encrypt (t,
+ GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED);
+ t_ax_encrypt (&t->ax,
&ax_msg[1],
message,
payload_size);
- ax_msg->Ns = htonl (t->ax.Ns++);
- ax_msg->PNs = htonl (t->ax.PNs);
+ ax_msg->ax_header.Ns = htonl (t->ax.Ns++);
+ ax_msg->ax_header.PNs = htonl (t->ax.PNs);
+ /* FIXME: we should do this once, not once per message;
+ this is a point multiplication, and DHRs does not
+ change all the time. */
GNUNET_CRYPTO_ecdhe_key_get_public (t->ax.DHRs,
- &ax_msg->DHRs);
- t_h_encrypt (t,
+ &ax_msg->ax_header.DHRs);
+ t_h_encrypt (&t->ax,
ax_msg);
- t_hmac (&ax_msg->Ns,
- AX_HEADER_SIZE + payload_size,
+ t_hmac (&ax_msg->ax_header,
+ sizeof (struct GNUNET_CADET_AxHeader) + payload_size,
0,
&t->ax.HKs,
&ax_msg->hmac);
- // ax_msg->pid = htonl (GCC_get_pid (c, fwd)); // FIXME: connection flow-control not (re)implemented yet!
tq = GNUNET_malloc (sizeof (*tq));
tq->t = t;
tq->env = env;
- tq->cid = &ax_msg->cid;
+ tq->cid = &ax_msg->cid; /* will initialize 'ax_msg->cid' once we know the connection */
tq->cont = cont;
tq->cont_cls = cont_cls;
GNUNET_CONTAINER_DLL_insert_tail (t->tq_head,
t->tq_tail,
tq);
- trigger_transmissions (t);
+ if (NULL != t->send_task)
+ GNUNET_SCHEDULER_cancel (t->send_task);
+ t->send_task
+ = GNUNET_SCHEDULER_add_now (&trigger_transmissions,
+ t);
return tq;
}
* function is called. Once the continuation is called, the message is
* no longer in the queue!
*
- * @param q Handle to the queue entry to cancel.
+ * @param tq Handle to the queue entry to cancel.
*/
void
-GCT_send_cancel (struct CadetTunnelQueueEntry *q)
+GCT_send_cancel (struct CadetTunnelQueueEntry *tq)
{
- struct CadetTunnel *t = q->t;
+ struct CadetTunnel *t = tq->t;
GNUNET_CONTAINER_DLL_remove (t->tq_head,
t->tq_tail,
- q);
- GNUNET_free (q);
+ tq);
+ GNUNET_MQ_discard (tq->env);
+ GNUNET_free (tq);
}
GCT_ConnectionIterator iter,
void *iter_cls)
{
- for (struct CadetTConnection *ct = t->connection_head;
+ struct CadetTConnection *n;
+ for (struct CadetTConnection *ct = t->connection_ready_head;
NULL != ct;
- ct = ct->next)
+ ct = n)
+ {
+ n = ct->next;
+ iter (iter_cls,
+ ct);
+ }
+ for (struct CadetTConnection *ct = t->connection_busy_head;
+ NULL != ct;
+ ct = n)
+ {
+ n = ct->next;
iter (iter_cls,
- ct->cc);
+ ct);
+ }
}
}
-/**
- * Get string description for tunnel connectivity state.
- *
- * @param cs Tunnel state.
- *
- * @return String representation.
- */
-static const char *
-cstate2s (enum CadetTunnelCState cs)
-{
- static char buf[32];
-
- switch (cs)
- {
- case CADET_TUNNEL_NEW:
- return "CADET_TUNNEL_NEW";
- case CADET_TUNNEL_SEARCHING:
- return "CADET_TUNNEL_SEARCHING";
- case CADET_TUNNEL_WAITING:
- return "CADET_TUNNEL_WAITING";
- case CADET_TUNNEL_READY:
- return "CADET_TUNNEL_READY";
- case CADET_TUNNEL_SHUTDOWN:
- return "CADET_TUNNEL_SHUTDOWN";
- default:
- SPRINTF (buf, "%u (UNKNOWN STATE)", cs);
- return buf;
- }
-}
-
-
-/**
- * Get string description for tunnel encryption state.
- *
- * @param es Tunnel state.
- *
- * @return String representation.
- */
-static const char *
-estate2s (enum CadetTunnelEState es)
-{
- static char buf[32];
-
- switch (es)
- {
- case CADET_TUNNEL_KEY_UNINITIALIZED:
- return "CADET_TUNNEL_KEY_UNINITIALIZED";
- case CADET_TUNNEL_KEY_SENT:
- return "CADET_TUNNEL_KEY_SENT";
- case CADET_TUNNEL_KEY_PING:
- return "CADET_TUNNEL_KEY_PING";
- case CADET_TUNNEL_KEY_OK:
- return "CADET_TUNNEL_KEY_OK";
- case CADET_TUNNEL_KEY_REKEY:
- return "CADET_TUNNEL_KEY_REKEY";
- default:
- SPRINTF (buf, "%u (UNKNOWN STATE)", es);
- return buf;
- }
-}
-
-
#define LOG2(level, ...) GNUNET_log_from_nocheck(level,"cadet-tun",__VA_ARGS__)
return;
LOG2 (level,
- "TTT TUNNEL TOWARDS %s in cstate %s, estate %s tq_len: %u #cons: %u\n",
+ "TTT TUNNEL TOWARDS %s in estate %s tq_len: %u #cons: %u\n",
GCT_2s (t),
- cstate2s (t->cstate),
estate2s (t->estate),
t->tq_len,
- t->num_connections);
-#if DUMP_KEYS_TO_STDERR
- ax_debug (t->ax, level);
-#endif
+ GCT_count_any_connections (t));
LOG2 (level,
"TTT channels:\n");
GNUNET_CONTAINER_multihashmap32_iterate (t->channels,
&level);
LOG2 (level,
"TTT connections:\n");
- for (iter_c = t->connection_head; NULL != iter_c; iter_c = iter_c->next)
+ for (iter_c = t->connection_ready_head; NULL != iter_c; iter_c = iter_c->next)
+ GCC_debug (iter_c->cc,
+ level);
+ for (iter_c = t->connection_busy_head; NULL != iter_c; iter_c = iter_c->next)
GCC_debug (iter_c->cc,
level);