struct GNUNET_CRYPTO_SymmetricSessionKey MK;
};
+
/**
- * Axolotl data, according to @url https://github.com/trevp/axolotl/wiki .
+ * Axolotl data, according to https://github.com/trevp/axolotl/wiki .
*/
struct CadetTunnelAxolotl
{
uint32_t PNs;
/**
- * True (#GNUNET_YES) if the party will send a new ratchet key in next msg.
+ * True (#GNUNET_YES) if we have to send a new ratchet key in next msg.
*/
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.
+ */
+ unsigned 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.
+ */
+ unsigned int ratchet_counter;
+
+ /**
+ * When does this ratchet expire and a new one is triggered.
+ */
+ struct GNUNET_TIME_Absolute ratchet_expiration;
};
/**
*/
static unsigned long long default_ttl;
-
/**
* Own Peer ID private key.
*/
const static struct GNUNET_CRYPTO_EddsaPrivateKey *id_key;
+
/******************************** AXOLOTL ************************************/
static struct GNUNET_CRYPTO_EcdhePrivateKey *ax_key;
*/
static struct CadetAxolotlSignedKey ax_identity;
-/******************************** OTR ***********************************/
+/**
+ * How many messages are needed to trigger a ratchet advance.
+ */
+static unsigned long long ratchet_messages;
+/**
+ * How long until we trigger a ratched advance.
+ */
+static struct GNUNET_TIME_Relative ratchet_time;
+
+
+/******************************** OTR ***********************************/
/**
* Own global OTR ephemeral private key.
ax = t->ax;
+ ax->ratchet_counter++;
+ if (GNUNET_YES == ax->ratchet_allowed
+ && (ratchet_messages <= ax->ratchet_counter
+ || 0 == GNUNET_TIME_absolute_get_remaining (ax->ratchet_expiration).rel_value_us))
+ {
+ ax->ratchet_flag = GNUNET_YES;
+ }
+
if (GNUNET_YES == ax->ratchet_flag)
{
/* Advance ratchet */
ax->PNs = ax->Ns;
ax->Ns = 0;
ax->ratchet_flag = GNUNET_NO;
+ ax->ratchet_allowed = GNUNET_NO;
+ ax->ratchet_counter = 0;
+ ax->ratchet_expiration =
+ GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get(), ratchet_time);
}
t_hmac_derive_key (&ax->CKs, &MK, "0", 1);
GNUNET_CRYPTO_symmetric_derive_iv (&iv, &MK, NULL, 0, NULL);
#if DUMP_KEYS_TO_STDERR
- LOG (GNUNET_ERROR_TYPE_INFO, " txt: %s\n",
- GNUNET_h2s ((struct GNUNET_HashCode *) src));
LOG (GNUNET_ERROR_TYPE_INFO, " CKs: %s\n",
GNUNET_h2s ((struct GNUNET_HashCode *) &ax->CKs));
- LOG (GNUNET_ERROR_TYPE_INFO, " AX_ENC with key %s\n",
+ LOG (GNUNET_ERROR_TYPE_INFO, " AX_ENC with key %u: %s\n", ax->Ns,
GNUNET_h2s ((struct GNUNET_HashCode *) &MK));
#endif
#if DUMP_KEYS_TO_STDERR
LOG (GNUNET_ERROR_TYPE_INFO, " CKr: %s\n",
GNUNET_h2s ((struct GNUNET_HashCode *) &ax->CKr));
- LOG (GNUNET_ERROR_TYPE_INFO, " AX_DEC with key %s\n",
+ LOG (GNUNET_ERROR_TYPE_INFO, " AX_DEC with key %u: %s\n", ax->Nr,
GNUNET_h2s ((struct GNUNET_HashCode *) &MK));
#endif
t_hmac_derive_key (&ax->CKr, &ax->CKr, "1", 1);
LOG (GNUNET_ERROR_TYPE_DEBUG, " t_ax_decrypt end\n");
- LOG (GNUNET_ERROR_TYPE_INFO, " txt: %s\n",
- GNUNET_h2s ((struct GNUNET_HashCode *) dst));
-
return out_size;
}
}
+/**
+ * Delete a key from the list of skipped keys.
+ *
+ * @param t Tunnel to delete from.
+ * @param HKr Header Key to use.
+ */
+static void
+store_skipped_key (struct CadetTunnel *t,
+ const struct GNUNET_CRYPTO_SymmetricSessionKey *HKr)
+{
+ struct CadetTunnelSkippedKey *key;
+
+ key = GNUNET_new (struct CadetTunnelSkippedKey);
+ key->timestamp = GNUNET_TIME_absolute_get ();
+ t_hmac_derive_key (&t->ax->CKr, &key->MK, "0", 1);
+ #if DUMP_KEYS_TO_STDERR
+ LOG (GNUNET_ERROR_TYPE_INFO, " storing MK for Nr %u: %s\n",
+ t->ax->Nr, GNUNET_h2s ((struct GNUNET_HashCode *) &key->MK));
+ LOG (GNUNET_ERROR_TYPE_INFO, " for CKr: %s\n",
+ GNUNET_h2s ((struct GNUNET_HashCode *) &t->ax->CKr));
+ #endif
+ t_hmac_derive_key (&t->ax->CKr, &t->ax->CKr, "1", 1);
+ GNUNET_CONTAINER_DLL_insert (t->ax->skipped_head, t->ax->skipped_tail, key);
+ t->ax->Nr++;
+ t->ax->skipped++;
+}
+
+
+/**
+ * Delete a key from the list of skipped keys.
+ *
+ * @param t Tunnel to delete from.
+ * @param key Key to delete.
+ */
+static void
+delete_skipped_key (struct CadetTunnel *t, struct CadetTunnelSkippedKey *key)
+{
+ GNUNET_CONTAINER_DLL_remove (t->ax->skipped_head, t->ax->skipped_tail, key);
+ GNUNET_free (key);
+ t->ax->skipped--;
+}
+
+
/**
* Stage skipped AX keys and calculate the message key.
*
*
* @param t Tunnel where to stage the keys.
* @param HKr Header key.
- * @param Nr Current message number.
* @param Np Received meesage number.
- * @param CKr[in/out] Chain key, gets ratcheted forward to the new state.
*/
static void
store_ax_keys (struct CadetTunnel *t,
const struct GNUNET_CRYPTO_SymmetricSessionKey *HKr,
- uint32_t Nr, uint32_t Np,
- struct GNUNET_CRYPTO_SymmetricSessionKey *CKr)
+ uint32_t Np)
{
- struct CadetTunnelSkippedKey *key;
- unsigned int i;
+ int gap;
- if (Np - Nr > MAX_KEY_GAP)
+ gap = Np - t->ax->Nr;
+ if (MAX_KEY_GAP < gap || 0 > gap)
{
/* Avoid DoS (forcing peer to do 2*33 chain HMAC operations) */
/* TODO: start new key exchange on return */
return;
}
- for (i = Nr; i < Np; i++)
- {
- key = GNUNET_new (struct CadetTunnelSkippedKey);
- key->timestamp = GNUNET_TIME_absolute_get ();
- t_hmac_derive_key (CKr, &key->MK, "0", 1);
- #if DUMP_KEYS_TO_STDERR
- LOG (GNUNET_ERROR_TYPE_INFO, " storing MK for Nr %u: %s\n",
- GNUNET_h2s ((struct GNUNET_HashCode *) &key->MK));
- LOG (GNUNET_ERROR_TYPE_INFO, " for CKr: %s\n",
- GNUNET_h2s ((struct GNUNET_HashCode *) &t->ax->CKr));
- #endif
- t_hmac_derive_key (CKr, CKr, "1", 1);
- GNUNET_CONTAINER_DLL_insert (t->ax->skipped_head, t->ax->skipped_tail, key);
- t->ax->skipped++;
- }
+ while (t->ax->Nr < Np)
+ store_skipped_key (t, HKr);
+
+ while (t->ax->skipped > MAX_SKIPPED_KEYS)
+ delete_skipped_key (t, t->ax->skipped_tail);
}
Np = ntohl (dstmsg->Ns);
PNp = ntohl (dstmsg->PNs);
DHRp = &dstmsg->DHRs;
- store_ax_keys (t, &HK, ax->Nr, PNp, &ax->CKr);
+ store_ax_keys (t, &HK, PNp);
/* RKp, NHKp, CKp = KDF (HMAC-HASH (RK, DH (DHRp, DHRs))) */
GNUNET_CRYPTO_ecc_ecdh (ax->DHRs, DHRp, &dh);
ax->CKr = keys[2];
ax->DHRr = *DHRp;
ax->Nr = 0;
+ ax->ratchet_allowed = GNUNET_YES;
}
else
{
PNp = ntohl (dstmsg->PNs);
}
- if (Np > ax->Nr + 1)
- store_ax_keys (t, &ax->HKr, ax->Nr, Np, &ax->CKr);
+ if (Np > ax->Nr)
+ store_ax_keys (t, &ax->HKr, Np);
- ax->Nr = Np;
+ ax->Nr = Np + 1;
osize = t_ax_decrypt (t, dst, &src[1], esize);
if (osize != esize)
LOG (GNUNET_ERROR_TYPE_DEBUG, "queue data on Tunnel %s\n", GCT_2s (t));
- if (GNUNET_YES == is_ready (t))
- {
- GNUNET_break (0);
- return NULL;
- }
+ GNUNET_assert (GNUNET_NO == is_ready (t));
tqd = GNUNET_malloc (sizeof (struct CadetTunnelDelayed) + size);
msg = &ax_msg->header;
msg->size = htons (sizeof (struct GNUNET_CADET_AX) + size);
msg->type = htons (GNUNET_MESSAGE_TYPE_CADET_AX);
+ ax_msg->reserved = 0;
esize = t_ax_encrypt (t, &ax_msg[1], message, size);
ax_msg->Ns = htonl (t->ax->Ns++);
ax_msg->PNs = htonl (t->ax->PNs);
t_hmac (&otr_msg[1], size, iv, select_key (t), &otr_msg->hmac);
msg->size = htons (sizeof (struct GNUNET_CADET_Encrypted) + size);
msg->type = htons (GNUNET_MESSAGE_TYPE_CADET_ENCRYPTED);
+ otr_msg->ttl = htonl (default_ttl);
}
GNUNET_assert (esize == size);
}
+/**
+ * @brief Resend the AX KX until we complete the handshake.
+ *
+ * @param cls Closure (tunnel).
+ * @param tc Task context.
+ */
+static void
+ax_kx_resend (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct CadetTunnel *t = cls;
+
+ t->rekey_task = NULL;
+
+ if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+ return;
+
+ if (CADET_TUNNEL_KEY_OK == t->estate)
+ return;
+
+ GCT_send_ax_kx (t, GNUNET_YES);
+}
+
+
/**
* Callback called when a queued message is sent.
*
*/
static void
ephm_sent (void *cls,
- struct CadetConnection *c,
- struct CadetConnectionQueue *q,
- uint16_t type, int fwd, size_t size)
+ struct CadetConnection *c,
+ struct CadetConnectionQueue *q,
+ uint16_t type, int fwd, size_t size)
{
struct CadetTunnel *t = cls;
LOG (GNUNET_ERROR_TYPE_DEBUG, "ephemeral sent %s\n", GC_m2s (type));
+
t->ephm_h = NULL;
+
+ if (CADET_TUNNEL_KEY_OK == t->estate)
+ return;
+
+ if (CADET_Axolotl == t->enc_type && CADET_TUNNEL_KEY_OK != t->estate)
+ {
+ if (NULL != t->rekey_task)
+ {
+ GNUNET_break (0);
+ GNUNET_SCHEDULER_cancel (t->rekey_task);
+ }
+ t->rekey_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
+ &ax_kx_resend, t);
+ }
}
+
/**
* Callback called when a queued message is sent.
*
t->pong_h = NULL;
}
+
/**
* Sends key exchange message on a tunnel, choosing the best connection.
* Should not be called on loopback tunnels.
GNUNET_free (t->ax);
t->ax = NULL;
+
+ if (NULL != t->rekey_task)
+ {
+ GNUNET_SCHEDULER_cancel (t->rekey_task);
+ t->rekey_task = NULL;
+ }
+ if (NULL != t->ephm_h)
+ {
+ GCC_cancel (t->ephm_h);
+ t->ephm_h = NULL;
+ }
}
return;
}
rekey_tunnel (t, NULL);
+ GNUNET_STATISTICS_update (stats, "# otr-downgrades", -1, GNUNET_NO);
}
/**
return;
}
+ if (GNUNET_YES == ntohl (msg->force_reply))
+ GCT_send_ax_kx (t, GNUNET_NO);
+
+ if (CADET_TUNNEL_KEY_OK == t->estate)
+ return;
+
LOG (GNUNET_ERROR_TYPE_INFO, " is Alice? %s\n", am_I_alice ? "YES" : "NO");
ax = t->ax;
ax->NHKs = keys[3];
ax->CKs = keys[4];
ax->ratchet_flag = GNUNET_NO;
+ ax->ratchet_allowed = GNUNET_NO;
+ ax->ratchet_counter = 0;
+ ax->ratchet_expiration =
+ GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get(), ratchet_time);
}
GCT_change_estate (t, CADET_TUNNEL_KEY_OK);
}
GNUNET_CONFIGURATION_get_value_number (c, "CADET", "DEFAULT_TTL",
&default_ttl))
{
- GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_DEBUG,
"CADET", "DEFAULT_TTL", "USING DEFAULT");
default_ttl = 64;
}
{
rekey_period = GNUNET_TIME_UNIT_DAYS;
}
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (c, "CADET", "RATCHET_MESSAGES",
+ &ratchet_messages))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
+ "CADET", "RATCHET_MESSAGES", "USING DEFAULT");
+ ratchet_messages = 64;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_time (c, "CADET", "RATCHET_TIME",
+ &ratchet_time))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
+ "CADET", "RATCHET_TIME", "USING DEFAULT");
+ ratchet_time = GNUNET_TIME_UNIT_HOURS;
+ }
+
id_key = key;
else if (CADET_TUNNEL_KEY_UNINITIALIZED == t->estate)
{
LOG (GNUNET_ERROR_TYPE_DEBUG, " cstate triggered kx\n");
- GCT_send_ax_kx (t);
+ GCT_send_ax_kx (t, GNUNET_NO);
}
else
{
* Send an Axolotl KX message.
*
* @param t Tunnel on which to send it.
+ * @param force_reply Force the other peer to reply with a KX message.
*/
void
-GCT_send_ax_kx (struct CadetTunnel *t)
+GCT_send_ax_kx (struct CadetTunnel *t, int force_reply)
{
struct GNUNET_CADET_AX_KX msg;
LOG (GNUNET_ERROR_TYPE_INFO, "===> AX_KX for %s\n", GCT_2s (t));
+ if (NULL != t->ephm_h)
+ {
+ LOG (GNUNET_ERROR_TYPE_INFO, " already queued\n");
+ return;
+ }
msg.header.size = htons (sizeof (msg));
msg.header.type = htons (GNUNET_MESSAGE_TYPE_CADET_AX_KX);
+ msg.force_reply = htonl (force_reply);
msg.permanent_key = ax_identity.permanent_key;
msg.purpose = ax_identity.purpose;
msg.signature = ax_identity.signature;
GNUNET_CRYPTO_ecdhe_key_get_public (t->ax->DHRs, &msg.ratchet_key);
t->ephm_h = send_kx (t, &msg.header);
- GCT_change_estate (t, CADET_TUNNEL_KEY_SENT);
+ if (CADET_TUNNEL_KEY_OK != t->estate)
+ GCT_change_estate (t, CADET_TUNNEL_KEY_SENT);
}