* Switch received. Committed; send data; move to @KXS_SWITCH@.
*/
-/*----- Tunable parameters ------------------------------------------------*/
-
-#define T_VALID SEC(20) /* Challenge validity period */
-#define T_RETRY SEC(10) /* Challenge retransmit interval */
-
-#define VALIDP(kx, now) ((now) < (kx)->t_valid)
-
/*----- Static tables -----------------------------------------------------*/
static const char *const pkname[] = {
/*----- Various utilities -------------------------------------------------*/
+/* --- @VALIDP@ --- *
+ *
+ * Arguments: @const keyexch *kx@ = key exchange state
+ * @time_t now@ = current time in seconds
+ *
+ * Returns: Whether the challenge in the key-exchange state is still
+ * valid or should be regenerated.
+ */
+
+#define VALIDP(kx, now) ((now) < (kx)->t_valid)
+
/* --- @hashge@ --- *
*
* Arguments: @ghash *h@ = pointer to hash context
/* --- @settimer@ --- *
*
* Arguments: @keyexch *kx@ = pointer to key exchange context
- * @time_t t@ = when to set the timer for
+ * @struct timeval *tv@ = when to set the timer for
*
* Returns: ---
*
* Use: Sets the timer for the next key exchange attempt.
*/
-static void settimer(keyexch *kx, time_t t)
+static void settimer(keyexch *kx, struct timeval *tv)
{
- struct timeval tv;
- if (kx->f & KXF_TIMER)
- sel_rmtimer(&kx->t);
- tv.tv_sec = t;
- tv.tv_usec = 0;
- sel_addtimer(&sel, &kx->t, &tv, timer, kx);
+ if (kx->f & KXF_TIMER) sel_rmtimer(&kx->t);
+ sel_addtimer(&sel, &kx->t, tv, timer, kx);
kx->f |= KXF_TIMER;
}
+/* --- @f2tv@ --- *
+ *
+ * Arguments: @struct timeval *tv@ = where to write the timeval
+ * @double t@ = a time as a floating point number
+ *
+ * Returns: ---
+ *
+ * Use: Converts a floating-point time into a timeval.
+ */
+
+static void f2tv(struct timeval *tv, double t)
+{
+ tv->tv_sec = t;
+ tv->tv_usec = (t - tv->tv_sec)*MILLION;
+}
+
+/* --- @wobble@ --- *
+ *
+ * Arguments: @double t@ = a time interval
+ *
+ * Returns: The same time interval, with a random error applied.
+ */
+
+static double wobble(double t)
+{
+ uint32 r = rand_global.ops->word(&rand_global);
+ double w = (r/F_2P32) - 0.5;
+ return (t + t*w*T_WOBBLE);
+}
+
+/* --- @rs_time@ --- *
+ *
+ * Arguments: @retry *rs@ = current retry state
+ * @struct timeval *tv@ = where to write the result
+ * @const struct timeval *now@ = current time, or null
+ *
+ * Returns: ---
+ *
+ * Use: Computes a time at which to retry sending a key-exchange
+ * packet. This algorithm is subject to change, but it's
+ * currently a capped exponential backoff, slightly randomized
+ * to try to keep clients from hammering a server that's only
+ * just woken up.
+ *
+ * If @now@ is null then the function works out the time for
+ * itself.
+ */
+
+static void rs_time(retry *rs, struct timeval *tv, const struct timeval *now)
+{
+ double t;
+ struct timeval rtv;
+
+ if (!rs->t)
+ t = SEC(2);
+ else {
+ t = (rs->t * 5)/4;
+ if (t > MIN(5)) t = MIN(5);
+ }
+ rs->t = t;
+
+ if (!now) {
+ now = tv;
+ gettimeofday(tv, 0);
+ }
+ f2tv(&rtv, wobble(t));
+ TV_ADD(tv, now, &rtv);
+}
+
+/* --- @retry_reset@ --- *
+ *
+ * Arguments: @retry *rs@ = retry state
+ *
+ * Returns: --
+ *
+ * Use: Resets a retry state to indicate that progress has been
+ * made. Also useful for initializing the state in the first
+ * place.
+ */
+
+static void rs_reset(retry *rs) { rs->t = 0; }
+
/*----- Challenge management ----------------------------------------------*/
/* --- Notes on challenge management --- *
kxc->kx = kx;
kxc->f = 0;
kx->r[i] = kxc;
+ rs_reset(&kxc->rs);
return (kxc);
}
if (kxc->f & KXF_TIMER)
sel_rmtimer(&kxc->t);
gettimeofday(&tv, 0);
- tv.tv_sec += T_RETRY;
+ rs_time(&kxc->rs, &tv, &tv);
sel_addtimer(&sel, &kxc->t, &tv, kxc_timer, kxc);
kxc->f |= KXF_TIMER;
}
kxchal *kxc;
buf bb;
stats *st = p_stats(kx->p);
+ struct timeval tv;
buf *b;
switch (kx->s) {
p_txend(kx->p);
}
- if (kx->s < KXS_SWITCH)
- settimer(kx, time(0) + T_RETRY);
+ if (kx->s < KXS_SWITCH) {
+ rs_time(&kx->rs, &tv, 0);
+ settimer(kx, &tv);
+ }
}
/* --- @decryptrest@ --- *
static void kxfinish(keyexch *kx)
{
kxchal *kxc = kx->r[0];
+ struct timeval now, tv;
+
ks_activate(kxc->ks);
- settimer(kx, ks_tregen(kxc->ks));
+ gettimeofday(&now, 0);
+ f2tv(&tv, wobble(T_REGEN));
+ TV_ADD(&tv, &now, &tv);
+ settimer(kx, &tv);
kx->s = KXS_SWITCH;
a_notify("KXDONE", "?PEER", kx->p, A_END);
p_stats(kx->p)->t_kx = time(0);
void kx_message(keyexch *kx, unsigned msg, buf *b)
{
- time_t now = time(0);
+ struct timeval now, tv;
stats *st = p_stats(kx->p);
size_t sz = BSZ(b);
int rc;
+ gettimeofday(&now, 0);
+ rs_reset(&kx->rs);
if (kx->f & KXF_CORK) {
- start(kx, now);
- settimer(kx, now + T_RETRY);
- a_notify("KXSTART", A_END);
+ start(kx, now.tv_sec);
+ rs_time(&kx->rs, &tv, &now);
+ settimer(kx, &tv);
+ a_notify("KXSTART", "?PEER", kx->p, A_END);
}
if (checkpub(kx))
return;
- if (!VALIDP(kx, now)) {
+ if (!VALIDP(kx, now.tv_sec)) {
stop(kx);
- start(kx, now);
+ start(kx, now.tv_sec);
}
T( trace(T_KEYEXCH, "keyexch: processing %s packet from `%s'",
msg < KX_NMSG ? pkname[msg] : "unknown", p_name(kx->p)); )
int kx_init(keyexch *kx, peer *p, keyset **ks, unsigned f)
{
- if ((kx->kpriv = km_findpriv(tag_priv)) == 0) goto fail_0;
+ if ((kx->kpriv = km_findpriv(p_privtag(p))) == 0) goto fail_0;
if ((kx->kpub = km_findpub(p_tag(p))) == 0) goto fail_1;
- if (!km_samealgsp(kx->kpriv, kx->kpub)) {
- a_warn("KX", "?PEER", kx->p, "algorithms-mismatch",
- "local-private-key", "%s", tag_priv,
+ if (!group_samep(kx->kpriv->g, kx->kpub->g)) {
+ a_warn("KX", "?PEER", p, "group-mismatch",
+ "local-private-key", "%s", p_privtag(p),
"peer-public-key", "%s", p_tag(p),
A_END);
goto fail_2;
kx->ks = ks;
kx->p = p;
kx->f = KXF_DEAD | KXF_PUBKEY | f;
+ rs_reset(&kx->rs);
if (!(kx->f & KXF_CORK)) {
start(kx, time(0));
resend(kx);