chiark / gitweb /
server/tests.at (AWAIT_KXDONE): Ignore the correct server messages.
[tripe] / server / keyexch.c
index 1527a297f8e1fcc4ad71b3cbe3e913b2d661b594..2502fa398d797578378448df7578914710d4f034 100644 (file)
  *     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[] = {
@@ -90,6 +83,17 @@ 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
@@ -269,24 +273,102 @@ static void timer(struct timeval *tv, void *v)
 /* --- @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 --- *
@@ -371,6 +453,7 @@ static kxchal *kxc_new(keyexch *kx)
   kxc->kx = kx;
   kxc->f = 0;
   kx->r[i] = kxc;
+  rs_reset(&kxc->rs);
   return (kxc);
 }
 
@@ -465,7 +548,7 @@ static void kxc_answer(keyexch *kx, kxchal *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;
 }
@@ -734,6 +817,7 @@ static void resend(keyexch *kx)
   kxchal *kxc;
   buf bb;
   stats *st = p_stats(kx->p);
+  struct timeval tv;
   buf *b;
 
   switch (kx->s) {
@@ -776,8 +860,10 @@ static void resend(keyexch *kx)
     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@ --- *
@@ -919,8 +1005,13 @@ bad:
 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);
@@ -1176,23 +1267,26 @@ void kx_start(keyexch *kx, int forcep)
 
 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)); )
@@ -1383,11 +1477,11 @@ newkeys:
 
 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", kx->p, "group-mismatch",
+          "local-private-key", "%s", p_privtag(p),
           "peer-public-key", "%s", p_tag(p),
           A_END);
     goto fail_2;
@@ -1396,6 +1490,7 @@ int kx_init(keyexch *kx, peer *p, keyset **ks, unsigned f)
   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);