* @author Christian Grothoff
*
* FIXME:
- * - check KX estate machine -- make sure it is never stuck!
- * - clean up KX logic, including adding sender authentication
- * - implement connection management (evaluate, kill old ones,
- * search for new ones)
- * - when managing connections, distinguish those that
- * have (recently) had traffic from those that were
- * never ready (or not recently)
+ * - KX:
+ * + clean up KX logic, including adding sender authentication
+ * + implement rekeying
+ * + check KX estate machine -- make sure it is never stuck!
+ * - connection management
+ * + properly (evaluate, kill old ones, search for new ones)
+ * + when managing connections, distinguish those that
+ * have (recently) had traffic from those that were
+ * never ready (or not recently)
*/
#include "platform.h"
#include "gnunet_util_lib.h"
#define LOG(level, ...) GNUNET_log_from(level,"cadet-tun",__VA_ARGS__)
-
/**
- * How long do we wait until tearing down an idle tunnel?
+ * How often do we try to decrypt payload with unverified key
+ * material? Used to limit CPU increase upon receiving bogus
+ * KX.
*/
-#define IDLE_DESTROY_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 90)
+#define MAX_UNVERIFIED_ATTEMPTS 16
/**
- * Yuck, replace by 'offsetof' expression?
- * FIXME.
+ * How long do we wait until tearing down an idle tunnel?
*/
-#define AX_HEADER_SIZE (sizeof (uint32_t) * 2\
- + sizeof (struct GNUNET_CRYPTO_EcdhePublicKey))
-
+#define IDLE_DESTROY_DELAY GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 90)
/**
* Maximum number of skipped keys we keep in memory per tunnel.
*/
struct CadetTunnelAxolotl ax;
+ /**
+ * 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.
+ */
+ struct CadetTunnelAxolotl *unverified_ax;
+
/**
* Task scheduled if there are no more channels using the tunnel.
*/
*/
struct CadetTunnelQueueEntry *tq_tail;
-
- /**
- * Ephemeral message in the queue (to avoid queueing more than one).
- */
- struct CadetConnectionQueue *ephm_hKILL;
-
- /**
- * Pong message in the queue.
- */
- struct CadetConnectionQueue *pong_hKILL;
-
/**
* How long do we wait until we retry the KX?
*/
*/
unsigned int num_connections;
+ /**
+ * 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 unverified_attempts;
+
/**
* Number of entries in the @e tq_head DLL.
*/
}
-/**
- * Create a new Axolotl ephemeral (ratchet) key.
- *
- * @param t Tunnel.
- */
-static void
-new_ephemeral (struct CadetTunnel *t)
-{
- GNUNET_free_non_null (t->ax.DHRs);
- t->ax.DHRs = GNUNET_CRYPTO_ecdhe_key_create ();
-}
-
-
-
/**
* Called when either we have a new connection, or a new message in the
* queue, or some existing connection has transmission capacity. Looks
/* ************************************** 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);
+ 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,
/**
* 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 ax key material to use.
* @param msg Message whose header to encrypt.
*/
static void
-t_h_encrypt (struct CadetTunnel *t,
+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.Ns,
+ sizeof (struct GNUNET_CADET_AxHeader),
&ax->HKs,
&iv,
- &msg->Ns);
- GNUNET_assert (AX_HEADER_SIZE == out_size);
+ &msg->ax_header.Ns);
+ 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,
+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_TunnelEncryptedMessage *src,
size_t size)
/* 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);
&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).
*/
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)
{
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_TunnelEncryptedMessage *src,
size_t size)
{
- struct CadetTunnelAxolotl *ax;
struct GNUNET_ShortHashCode msg_hmac;
struct GNUNET_HashCode hmac;
struct GNUNET_CADET_TunnelEncryptedMessage plaintext_header;
size_t esize; /* Size of encryped payload */
esize = size - sizeof (struct GNUNET_CADET_TunnelEncryptedMessage);
- ax = &t->ax;
/* 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);
* FIXME: does not take care of sender-authentication yet!
*
* @param t Tunnel on which to send it.
+ * @param ax axolotl key context to use
* @param force_reply Force the other peer to reply with a KX message.
*/
static void
send_kx (struct CadetTunnel *t,
+ struct CadetTunnelAxolotl *ax,
int force_reply)
{
- struct CadetTunnelAxolotl *ax = &t->ax;
struct CadetTConnection *ct;
struct CadetConnection *cc;
struct GNUNET_MQ_Envelope *env;
/**
- * Handle KX message.
+ * Cleanup state used by @a ax.
*
- * FIXME: sender-authentication in KX is missing!
+ * @param ax state to free, but not memory of @a ax itself
+ */
+static void
+cleanup_ax (struct CadetTunnelAxolotl *ax)
+{
+ 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);
+}
+
+
+/**
+ * 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
const struct GNUNET_CADET_TunnelKeyExchangeMessage *msg)
{
struct CadetTunnel *t = ct->t;
- struct CadetTunnelAxolotl *ax = &t->ax;
+ struct CadetTunnelAxolotl *ax;
struct GNUNET_HashCode key_material[3];
struct GNUNET_CRYPTO_SymmetricSessionKey keys[5];
const char salt[] = "CADET Axolotl salt";
const struct GNUNET_PeerIdentity *pid;
int am_I_alice;
+ /* We only keep ONE unverified KX around, so if there is an existing one,
+ clean it up. */
+ if (NULL != t->unverified_ax)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Dropping old unverified KX state, got a fresh one.\n",
+ t->unverified_attempts);
+ cleanup_ax (t->unverified_ax);
+ memset (t->unverified_ax,
+ 0,
+ sizeof (struct CadetTunnelAxolotl));
+ new_ephemeral (t->unverified_ax);
+ t->unverified_ax->kx_0 = GNUNET_CRYPTO_ecdhe_key_create ();
+ }
+ else
+ {
+ t->unverified_ax = GNUNET_new (struct CadetTunnelAxolotl);
+ new_ephemeral (t->unverified_ax);
+ t->unverified_ax->kx_0 = GNUNET_CRYPTO_ecdhe_key_create ();
+ }
+ t->unverified_attempts = 0;
+ ax = t->unverified_ax;
+
pid = GCP_get_id (t->destination);
if (0 > GNUNET_CRYPTO_cmp_peer_identity (&my_full_id,
pid))
t->kx_task = NULL;
}
send_kx (t,
+ ax,
GNUNET_NO);
}
}
+/**
+ * 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;
+
+ GNUNET_CONTAINER_DLL_remove (t->connection_head,
+ t->connection_tail,
+ ct);
+ GNUNET_free (ct);
+}
+
+
/**
* This tunnel is no longer used, destroy it.
*
GNUNET_assert (0 == GNUNET_CONTAINER_multihashmap32_size (t->channels));
while (NULL != (ct = t->connection_head))
{
+ struct CadetConnection *cc;
+
GNUNET_assert (ct->t == t);
- GNUNET_CONTAINER_DLL_remove (t->connection_head,
- t->connection_tail,
- ct);
- GCC_destroy (ct->cc);
- GNUNET_free (ct);
+ cc = ct->cc;
+ GCT_connection_lost (ct);
+ GCC_destroy_without_tunnel (cc);
}
while (NULL != (tq = t->tq_head))
{
}
GNUNET_MST_destroy (t->mst);
GNUNET_MQ_destroy (t->mq);
- while (NULL != t->ax.skipped_head)
- delete_skipped_key (t,
- t->ax.skipped_head);
- GNUNET_assert (0 == t->ax.skipped);
- GNUNET_free_non_null (t->ax.kx_0);
- GNUNET_free_non_null (t->ax.DHRs);
+ cleanup_ax (&t->ax);
+ if (NULL != t->unverified_ax)
+ {
+ cleanup_ax (t->unverified_ax);
+ GNUNET_free (t->unverified_ax);
+ }
GNUNET_free (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);
+ return GNUNET_OK;
+}
+
+
/**
* Destroys the tunnel @a t now, without delay. Used during shutdown.
*
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 ==
GNUNET_CONTAINER_multihashmap32_size (t->channels));
if (NULL != t->destroy_task)
t->kx_task = NULL;
send_kx (t,
+ &t->ax,
( (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate) ||
(CADET_TUNNEL_KEY_SENT == t->estate) )
? GNUNET_YES
{
/* no messages pending right now */
LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Not sending payload of tunnel %s on ready connection %s (nothing pending)\n",
+ "Not sending payload of %s on ready %s (nothing pending)\n",
GCT_2s (t),
GCC_2s (ct->cc));
return;
*tq->cid = *GCC_get_id (ct->cc);
ct->is_ready = GNUNET_NO;
LOG (GNUNET_ERROR_TYPE_DEBUG,
- "Sending payload of tunnel %s on connection %s\n",
+ "Sending payload of %s on %s\n",
GCT_2s (t),
GCC_2s (ct->cc));
GCC_transmit (ct->cc,
{
case CADET_TUNNEL_KEY_UNINITIALIZED:
send_kx (t,
+ &t->ax,
GNUNET_YES);
break;
case CADET_TUNNEL_KEY_SENT:
break;
case CADET_TUNNEL_KEY_REKEY:
send_kx (t,
+ &t->ax,
GNUNET_NO);
t->estate = CADET_TUNNEL_KEY_OK;
break;
/**
- * NOT IMPLEMENTED.
+ * 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
{
struct CadetTunnel *t = cls;
- GNUNET_break (0); // FIXME
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received KEEPALIVE on tunnel %s\n",
+ GCT_2s (t));
+ GNUNET_STATISTICS_update (stats,
+ "# keepalives received",
+ 1,
+ GNUNET_NO);
}
/* 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, sending DESTROY\n",
- GCCH_2s (ch));
+ "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,
- "Receicved channel OPEN_ACK on channel %s from %s\n",
+ "Received channel OPEN_ACK on channel %s from %s\n",
GCCH_2s (ch),
GCT_2s (t));
GCCH_handle_channel_open_ack (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. Ignoring.\n",
- GCCH_2s (ch));
+ "Received channel DESTORY for unknown channel %u. Ignoring.\n",
+ ntohl (cm->ctn.cn));
return;
}
LOG (GNUNET_ERROR_TYPE_DEBUG,
GNUNET_MQ_handler_end ()
};
- new_ephemeral (t);
+ new_ephemeral (&t->ax);
t->ax.kx_0 = GNUNET_CRYPTO_ecdhe_key_create ();
t->destination = destination;
t->channels = GNUNET_CONTAINER_multihashmap32_create (8);
* @param t a tunnel
* @param cid connection identifer to use for the connection
* @param path path to use for the connection
+ * @return #GNUNET_OK on success,
+ * #GNUNET_SYSERR on failure (duplicate connection)
*/
-void
+int
GCT_add_inbound_connection (struct CadetTunnel *t,
const struct GNUNET_CADET_ConnectionTunnelIdentifier *cid,
struct CadetPeerPath *path)
cid,
&connection_ready_cb,
ct);
+ if (NULL == ct->cc)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Tunnel %s refused inbound connection %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!)
"Tunnel %s has new connection %s\n",
GCT_2s (t),
GCC_2s (ct->cc));
+ return GNUNET_OK;
}
"# received encrypted",
1,
GNUNET_NO);
- decrypted_size = t_ax_decrypt_and_validate (t,
- cbuf,
- msg,
- size);
+ decrypted_size = -1;
+ if ( (CADET_TUNNEL_KEY_OK == t->estate) ||
+ (CADET_TUNNEL_KEY_REKEY == 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! */
+ cleanup_ax (&t->ax);
+ t->ax = *t->unverified_ax;
+ GNUNET_free (t->unverified_ax);
+ t->unverified_ax = NULL;
+ }
+ if (CADET_TUNNEL_KEY_PING == 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)
+ {
+ /* 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)
+ {
+ 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,
"Tunnel %s failed to decrypt and validate encrypted data\n",
GNUNET_NO);
return;
}
- if (CADET_TUNNEL_KEY_PING == t->estate)
- {
- 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);
- }
+
/* The MST will ultimately call #handle_decrypted() on each message. */
GNUNET_break_op (GNUNET_OK ==
GNUNET_MST_from_buffer (t->mst,
* @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,
struct GNUNET_MQ_Envelope *env;
struct GNUNET_CADET_TunnelEncryptedMessage *ax_msg;
+ if ( (CADET_TUNNEL_KEY_OK != t->estate) &&
+ (CADET_TUNNEL_KEY_REKEY != t->estate) )
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
payload_size = ntohs (message->size);
LOG (GNUNET_ERROR_TYPE_DEBUG,
"Encrypting %u bytes for tunnel %s\n",
env = GNUNET_MQ_msg_extra (ax_msg,
payload_size,
GNUNET_MESSAGE_TYPE_CADET_TUNNEL_ENCRYPTED);
- t_ax_encrypt (t,
+ 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);
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);
estate2s (t->estate),
t->tq_len,
t->num_connections);
-#if DUMP_KEYS_TO_STDERR
- ax_debug (t->ax, level);
-#endif
LOG2 (level,
"TTT channels:\n");
GNUNET_CONTAINER_multihashmap32_iterate (t->channels,