chiark / gitweb /
sig: Move hashing into algorithm
[secnet.git] / site.c
diff --git a/site.c b/site.c
index acbc2b62c99b9ed43e48b43a3d473becfdd404c3..df0ab3a46433f41f3f506a00bae751f61cbd5dae 100644 (file)
--- a/site.c
+++ b/site.c
 #define SITE_SENTMSG5 7
 #define SITE_WAIT     8
 
+#define CASES_MSG3_KNOWN LABEL_MSG3: case LABEL_MSG3BIS
+
 int32_t site_max_start_pad = 4*4;
 
 static cstring_t state_name(uint32_t state)
@@ -310,18 +312,18 @@ struct site {
     struct resolver_if *resolver;
     struct log_if *log;
     struct random_if *random;
-    struct rsaprivkey_if *privkey;
-    struct rsapubkey_if *pubkey;
+    struct sigprivkey_if *privkey;
+    struct sigpubkey_if *pubkey;
     struct transform_if **transforms;
     int ntransforms;
     struct dh_if *dh;
-    struct hash_if *hash;
 
     uint32_t index; /* Index of this site */
+    uint32_t early_capabilities;
     uint32_t local_capabilities;
     int32_t setup_retries; /* How many times to send setup packets */
     int32_t setup_retry_interval; /* Initial timeout for setup packets */
-    int32_t wait_timeout; /* How long to wait if setup unsuccessful */
+    int32_t wait_timeout_mean; /* How long to wait if setup unsuccessful */
     int32_t mobile_peer_expiry; /* How long to remember 2ary addresses */
     int32_t key_lifetime; /* How long a key lasts once set up */
     int32_t key_renegotiate_time; /* If we see traffic (or a keepalive)
@@ -505,10 +507,10 @@ static void dispose_transform(struct transform_inst_if **transform_var)
 
 static _Bool type_is_msg34(uint32_t type)
 {
-    return
-       type == LABEL_MSG3 ||
-       type == LABEL_MSG3BIS ||
-       type == LABEL_MSG4;
+    switch (type) {
+       case CASES_MSG3_KNOWN: case LABEL_MSG4: return True;
+       default: return False;
+    }
 }
 
 struct parsedname {
@@ -531,10 +533,19 @@ struct msg {
     int32_t pklen;
     char *pk;
     int32_t hashlen;
-    int32_t siglen;
-    char *sig;
+    struct alg_msg_data sig;
 };
 
+static int32_t wait_timeout(struct site *st) {
+    int32_t t = st->wait_timeout_mean;
+    int8_t factor;
+    if (t < INT_MAX/2) {
+       st->random->generate(st->random->st,sizeof(factor),&factor);
+       t += (t / 256) * factor;
+    }
+    return t;
+}
+
 static _Bool set_new_transform(struct site *st, char *pk)
 {
     _Bool ok;
@@ -565,7 +576,7 @@ static _Bool set_new_transform(struct site *st, char *pk)
 
     slog(st,LOG_SETUP_INIT,"key exchange negotiated transform"
         " %d (capabilities ours=%#"PRIx32" theirs=%#"PRIx32")",
-        st->chosen_transform->capab_transformnum,
+        st->chosen_transform->capab_bit,
         st->local_capabilities, st->remote_capabilities);
     return True;
 }
@@ -602,9 +613,8 @@ static void append_string_xinfo_done(struct buffer_if *buf,
    out using a transform of config data supplied by netlink */
 static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what)
 {
-    void *hst;
-    uint8_t *hash;
-    string_t dhpub, sig;
+    string_t dhpub;
+    unsigned minor;
 
     st->retries=st->setup_retries;
     BUF_ALLOC(&st->buffer,what);
@@ -616,7 +626,8 @@ static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what)
 
     struct xinfoadd xia;
     append_string_xinfo_start(&st->buffer,&xia,st->localname);
-    if ((st->local_capabilities & CAPAB_EARLY) || (type != LABEL_MSG1)) {
+    if ((st->local_capabilities & st->early_capabilities) ||
+       (type != LABEL_MSG1)) {
        buf_append_uint32(&st->buffer,st->local_capabilities);
     }
     if (type_is_msg34(type)) {
@@ -632,21 +643,25 @@ static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what)
 
     if (hacky_par_mid_failnow()) return False;
 
-    if (type==LABEL_MSG3BIS)
-       buf_append_uint8(&st->buffer,st->chosen_transform->capab_transformnum);
+    if (MSGMAJOR(type) == 3) do {
+       minor = MSGMINOR(type);
+       if (minor < 1) break;
+       buf_append_uint8(&st->buffer,st->chosen_transform->capab_bit);
+    } while (0);
 
     dhpub=st->dh->makepublic(st->dh->st,st->dhsecret,st->dh->len);
     buf_append_string(&st->buffer,dhpub);
     free(dhpub);
-    hash=safe_malloc(st->hash->len, "generate_msg");
-    hst=st->hash->init();
-    st->hash->update(hst,st->buffer.start,st->buffer.size);
-    st->hash->final(hst,hash);
-    sig=st->privkey->sign(st->privkey->st,hash,st->hash->len);
-    buf_append_string(&st->buffer,sig);
-    free(sig);
-    free(hash);
+
+    bool_t ok=st->privkey->sign(st->privkey->st,
+                               st->buffer.start,
+                               st->buffer.size,
+                               &st->buffer);
+    if (!ok) goto fail;
     return True;
+
+ fail:
+    return False;
 }
 
 static bool_t unpick_name(struct buffer_if *msg, struct parsedname *nm)
@@ -668,6 +683,8 @@ static bool_t unpick_name(struct buffer_if *msg, struct parsedname *nm)
 static bool_t unpick_msg(struct site *st, uint32_t type,
                         struct buffer_if *msg, struct msg *m)
 {
+    unsigned minor;
+
     m->capab_transformnum=-1;
     m->hashstart=msg->start;
     CHECK_AVAIL(msg,4);
@@ -703,28 +720,30 @@ static bool_t unpick_msg(struct site *st, uint32_t type,
        CHECK_EMPTY(msg);
        return True;
     }
-    if (type==LABEL_MSG3BIS) {
-       CHECK_AVAIL(msg,1);
-       m->capab_transformnum = buf_unprepend_uint8(msg);
-    } else {
-       m->capab_transformnum = CAPAB_TRANSFORMNUM_ANCIENT;
-    }
+    if (MSGMAJOR(type) == 3) do {
+       minor = MSGMINOR(type);
+#define MAYBE_READ_CAP(minminor, kind, dflt) do {                      \
+    if (minor < (minminor))                                            \
+       m->capab_##kind##num = (dflt);                                  \
+    else {                                                             \
+       CHECK_AVAIL(msg, 1);                                            \
+       m->capab_##kind##num = buf_unprepend_uint8(msg);                \
+    }                                                                  \
+} while (0)
+       MAYBE_READ_CAP(1, transform, CAPAB_BIT_ANCIENTTRANSFORM);
+#undef MAYBE_READ_CAP
+    } while (0);
     CHECK_AVAIL(msg,2);
     m->pklen=buf_unprepend_uint16(msg);
     CHECK_AVAIL(msg,m->pklen);
     m->pk=buf_unprepend(msg,m->pklen);
     m->hashlen=msg->start-m->hashstart;
-    CHECK_AVAIL(msg,2);
-    m->siglen=buf_unprepend_uint16(msg);
-    CHECK_AVAIL(msg,m->siglen);
-    m->sig=buf_unprepend(msg,m->siglen);
-    CHECK_EMPTY(msg);
 
-    /* In `process_msg3_msg4' below, we assume that we can write a nul
-     * terminator following the signature.  Make sure there's enough space.
-     */
-    if (msg->start >= msg->base + msg->alloclen)
+    if (!st->pubkey->unpick(st->pubkey->st,msg,&m->sig)) {
        return False;
+    }
+
+    CHECK_EMPTY(msg);
 
     return True;
 }
@@ -757,13 +776,13 @@ static bool_t check_msg(struct site *st, uint32_t type, struct msg *m,
        return False;
     }
     if (type==LABEL_MSG2) return True;
-    if (!consttime_memeq(m->nR,st->remoteN,NONCELEN)!=0) {
+    if (!consttime_memeq(m->nR,st->remoteN,NONCELEN)) {
        *error="wrong remotely-generated nonce";
        return False;
     }
     /* MSG3 has complicated rules about capabilities, which are
      * handled in process_msg3. */
-    if (type==LABEL_MSG3 || type==LABEL_MSG3BIS) return True;
+    if (MSGMAJOR(type) == 3) return True;
     if (m->remote_capabilities!=st->remote_capabilities) {
        *error="remote capabilities changed";
        return False;
@@ -814,25 +833,32 @@ static bool_t process_msg2(struct site *st, struct buffer_if *msg2,
 
     /* Select the transform to use */
 
-    uint32_t remote_transforms = st->remote_capabilities & CAPAB_TRANSFORM_MASK;
-    if (!remote_transforms)
+    uint32_t remote_crypto_caps = st->remote_capabilities & CAPAB_TRANSFORM_MASK;
+    if (!remote_crypto_caps)
        /* old secnets only had this one transform */
-       remote_transforms = 1UL << CAPAB_TRANSFORMNUM_ANCIENT;
+       remote_crypto_caps = 1UL << CAPAB_BIT_ANCIENTTRANSFORM;
+
+#define CHOOSE_CRYPTO(kind, whats) do {                                        \
+    struct kind##_if *iface;                                           \
+    uint32_t bit, ours = 0;                                            \
+    int i;                                                             \
+    for (i= 0; i < st->n##kind##s; i++) {                              \
+       iface=st->kind##s[i];                                           \
+       bit = 1UL << iface->capab_bit;                                  \
+       if (bit & remote_crypto_caps) goto kind##_found;                \
+       ours |= bit;                                                    \
+    }                                                                  \
+    slog(st,LOG_ERROR,"no " whats " in common"                         \
+        " (us %#"PRIx32"; them: %#"PRIx32")",                          \
+        st->local_capabilities & ours, remote_crypto_caps);            \
+    return False;                                                      \
+kind##_found:                                                          \
+    st->chosen_##kind = iface;                                         \
+} while (0)
 
-    struct transform_if *ti;
-    int i;
-    for (i=0; i<st->ntransforms; i++) {
-       ti=st->transforms[i];
-       if ((1UL << ti->capab_transformnum) & remote_transforms)
-           goto transform_found;
-    }
-    slog(st,LOG_ERROR,"no transforms in common"
-        " (us %#"PRIx32"; them: %#"PRIx32")",
-        st->local_capabilities & CAPAB_TRANSFORM_MASK,
-        remote_transforms);
-    return False;
- transform_found:
-    st->chosen_transform=ti;
+    CHOOSE_CRYPTO(transform, "transforms");
+
+#undef CHOOSE_CRYPTO
 
     memcpy(st->remoteN,m.nR,NONCELEN);
     return True;
@@ -844,29 +870,21 @@ static bool_t generate_msg3(struct site *st)
        and create message number 3. */
     st->random->generate(st->random->st,st->dh->len,st->dhsecret);
     return generate_msg(st,
-                       (st->remote_capabilities & CAPAB_TRANSFORM_MASK
-                        ? LABEL_MSG3BIS : LABEL_MSG3),
+                       (st->remote_capabilities & CAPAB_TRANSFORM_MASK)
+                       ? LABEL_MSG3BIS
+                       : LABEL_MSG3,
                        "site:MSG3");
 }
 
 static bool_t process_msg3_msg4(struct site *st, struct msg *m)
 {
-    uint8_t *hash;
-    void *hst;
-
     /* Check signature and store g^x mod m */
-    hash=safe_malloc(st->hash->len, "process_msg3_msg4");
-    hst=st->hash->init();
-    st->hash->update(hst,m->hashstart,m->hashlen);
-    st->hash->final(hst,hash);
-    /* Terminate signature with a '0' - already checked that this will fit */
-    m->sig[m->siglen]=0;
-    if (!st->pubkey->check(st->pubkey->st,hash,st->hash->len,m->sig)) {
+    if (!st->pubkey->check(st->pubkey->st,
+                          m->hashstart,m->hashlen,
+                          &m->sig)) {
        slog(st,LOG_SEC,"msg3/msg4 signature failed check!");
-       free(hash);
        return False;
     }
-    free(hash);
 
     st->remote_adv_mtu=m->remote_mtu;
 
@@ -879,7 +897,10 @@ static bool_t process_msg3(struct site *st, struct buffer_if *msg3,
     struct msg m;
     cstring_t err;
 
-    assert(msgtype==LABEL_MSG3 || msgtype==LABEL_MSG3BIS);
+    switch (msgtype) {
+       case CASES_MSG3_KNOWN: break;
+       default: assert(0);
+    }
 
     if (!unpick_msg(st,msgtype,msg3,&m)) return False;
     if (!check_msg(st,msgtype,&m,&err)) {
@@ -887,31 +908,46 @@ static bool_t process_msg3(struct site *st, struct buffer_if *msg3,
        return False;
     }
     uint32_t capab_adv_late = m.remote_capabilities
-       & ~st->remote_capabilities & CAPAB_EARLY;
+       & ~st->remote_capabilities & st->early_capabilities;
     if (capab_adv_late) {
        slog(st,LOG_SEC,"msg3 impermissibly adds early capability flag(s)"
             " %#"PRIx32" (was %#"PRIx32", now %#"PRIx32")",
             capab_adv_late, st->remote_capabilities, m.remote_capabilities);
        return False;
     }
-    st->remote_capabilities|=m.remote_capabilities;
 
-    struct transform_if *ti;
-    int i;
-    for (i=0; i<st->ntransforms; i++) {
-       ti=st->transforms[i];
-       if (ti->capab_transformnum == m.capab_transformnum)
-           goto transform_found;
-    }
-    slog(st,LOG_SEC,"peer chose unknown-to-us transform %d!",
-        m.capab_transformnum);
-    return False;
- transform_found:
-    st->chosen_transform=ti;
+#define CHOSE_CRYPTO(kind, what) do {                                  \
+    struct kind##_if *iface;                                           \
+    int i;                                                             \
+    for (i=0; i<st->n##kind##s; i++) {                                 \
+       iface=st->kind##s[i];                                           \
+       if (iface->capab_bit == m.capab_##kind##num)                    \
+           goto kind##_found;                                          \
+    }                                                                  \
+    slog(st,LOG_SEC,"peer chose unknown-to-us " what " %d!",           \
+        m.capab_##kind##num);                                                  \
+    return False;                                                      \
+kind##_found:                                                          \
+    st->chosen_##kind=iface;                                           \
+} while (0)
+
+    CHOSE_CRYPTO(transform, "transform");
+
+#undef CHOSE_CRYPTO
 
     if (!process_msg3_msg4(st,&m))
        return False;
 
+    /* Update our idea of the remote site's capabilities, now that we've
+     * verified that its message was authentic.
+     *
+     * Our previous idea of the remote site's capabilities came from the
+     * unauthenticated MSG1.  We've already checked that this new message
+     * doesn't change any of the bits we relied upon in the past, but it may
+     * also have set additional capability bits.  We simply throw those away
+     * now, and use the authentic capabilities from this MSG3. */
+    st->remote_capabilities=m.remote_capabilities;
+
     /* Terminate their DH public key with a '0' */
     m.pk[m.pklen]=0;
     /* Invent our DH secret key */
@@ -1158,6 +1194,14 @@ static bool_t process_msg0(struct site *st, struct buffer_if *msg0,
     transform_apply_return problem;
 
     problem = decrypt_msg0(st,msg0,src);
+    if (problem==transform_apply_seqdupe) {
+       /* We recently received another copy of this packet, maybe due
+        * to polypath.  That's not a problem; indeed, for the
+        * purposes of transport address management it is a success.
+        * But we don't want to process the packet. */
+       transport_data_msgok(st,src);
+       return False;
+    }
     if (problem)
        return False;
 
@@ -1345,7 +1389,7 @@ static void decrement_resolving_count(struct site *st, int by)
        } else if (st->local_mobile) {
            /* Not very good.  We should queue (another) renegotiation
             * so that we can update the peer address. */
-           st->key_renegotiate_time=st->now+st->wait_timeout;
+           st->key_renegotiate_time=st->now+wait_timeout(st);
        } else {
            slog(st,LOG_SETUP_INIT,"resolution failed: "
                 " continuing to use source address of peer's packets");
@@ -1458,7 +1502,8 @@ static void set_link_quality(struct site *st)
 
 static void enter_state_run(struct site *st)
 {
-    slog(st,LOG_STATE,"entering state RUN");
+    slog(st,LOG_STATE,"entering state RUN%s",
+        current_valid(st) ? " (keyed)" : " (unkeyed)");
     st->state=SITE_RUN;
     st->timeout=0;
 
@@ -1620,7 +1665,7 @@ static bool_t send_msg7(struct site *st, cstring_t reason)
 static void enter_state_wait(struct site *st)
 {
     slog(st,LOG_STATE,"entering state WAIT");
-    st->timeout=st->now+st->wait_timeout;
+    st->timeout=st->now+wait_timeout(st);
     st->state=SITE_WAIT;
     set_link_quality(st);
     BUF_FREE(&st->buffer); /* will have had an outgoing packet in it */
@@ -1762,14 +1807,34 @@ static bool_t named_for_us(struct site *st, const struct buffer_if *buf_in,
 }
 
 static bool_t we_have_priority(struct site *st, const struct msg *m) {
-    if ((st->local_capabilities & m->remote_capabilities)
-       && CAPAB_PRIORITY_MOBILE) {
+    if (st->local_capabilities & m->remote_capabilities &
+       CAPAB_PRIORITY_MOBILE) {
        if (st->local_mobile) return True;
        if (st-> peer_mobile) return False;
     }
     return st->our_name_later;
 }
 
+static bool_t setup_late_msg_ok(struct site *st, 
+                               const struct buffer_if *buf_in,
+                               uint32_t msgtype,
+                               const struct comm_addr *source) {
+    /* For setup packets which seem from their type like they are
+     * late.  Maybe they came via a different path.  All we do is make
+     * a note of the sending address, iff they look like they are part
+     * of the current key setup attempt. */
+    struct msg m;
+    if (!named_for_us(st,buf_in,msgtype,&m))
+       /* named_for_us calls unpick_msg which gets the nonces */
+       return False;
+    if (!consttime_memeq(m.nR,st->remoteN,NONCELEN) ||
+       !consttime_memeq(m.nL,st->localN, NONCELEN))
+       /* spoof ?  from stale run ?  who knows */
+       return False;
+    transport_setup_msgok(st,source);
+    return True;
+}
+
 /* This function is called by the communication device to deliver
    packets from our peers.
    It should return True if the packet is recognised as being for
@@ -1831,9 +1896,21 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf,
                BUF_FREE(buf);
                return True;
            }
+       } else if (st->state==SITE_SENTMSG2 ||
+                  st->state==SITE_SENTMSG4) {
+           if (consttime_memeq(named_msg.nR,st->remoteN,NONCELEN)) {
+               /* We are ahead in the protocol, but that msg1 had the
+                * peer's nonce so presumably it is from this key
+                * exchange run, via a slower route */
+               transport_setup_msgok(st,source);
+           } else {
+               slog(st,LOG_UNEXPECTED,"competing incoming message 1");
+           }
+           BUF_FREE(buf);
+           return True;
        }
        /* The message 1 was received at an unexpected stage of the
-          key setup. XXX POLICY - what do we do? */
+          key setup.  Well, they lost the race. */
        slog(st,LOG_UNEXPECTED,"unexpected incoming message 1");
        BUF_FREE(buf);
        return True;
@@ -1878,6 +1955,10 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf,
        case LABEL_MSG2:
            /* Setup packet: expected only in state SENTMSG1 */
            if (st->state!=SITE_SENTMSG1) {
+               if ((st->state==SITE_SENTMSG3 ||
+                    st->state==SITE_SENTMSG5) &&
+                   setup_late_msg_ok(st,buf,msgtype,source))
+                   break;
                slog(st,LOG_UNEXPECTED,"unexpected MSG2");
            } else if (process_msg2(st,buf,source)) {
                transport_setup_msgok(st,source);
@@ -1886,10 +1967,12 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf,
                slog(st,LOG_SEC,"invalid MSG2");
            }
            break;
-       case LABEL_MSG3:
-       case LABEL_MSG3BIS:
+       case CASES_MSG3_KNOWN:
            /* Setup packet: expected only in state SENTMSG2 */
            if (st->state!=SITE_SENTMSG2) {
+               if ((st->state==SITE_SENTMSG4) &&
+                   setup_late_msg_ok(st,buf,msgtype,source))
+                   break;
                slog(st,LOG_UNEXPECTED,"unexpected MSG3");
            } else if (process_msg3(st,buf,source,msgtype)) {
                transport_setup_msgok(st,source);
@@ -1901,6 +1984,9 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf,
        case LABEL_MSG4:
            /* Setup packet: expected only in state SENTMSG3 */
            if (st->state!=SITE_SENTMSG3) {
+               if ((st->state==SITE_SENTMSG5) &&
+                   setup_late_msg_ok(st,buf,msgtype,source))
+                   break;
                slog(st,LOG_UNEXPECTED,"unexpected MSG4");
            } else if (process_msg4(st,buf,source)) {
                transport_setup_msgok(st,source);
@@ -2051,6 +2137,7 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
     assert(index_sequence < 0xffffffffUL);
     st->index = ++index_sequence;
     st->local_capabilities = 0;
+    st->early_capabilities = CAPAB_PRIORITY_MOBILE;
     st->netlink=find_cl_if(dict,"link",CL_NETLINK,True,"site",loc);
 
 #define GET_CLOSURE_LIST(dictkey,things,nthings,CL_TYPE) do{           \
@@ -2085,17 +2172,22 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
     st->log=find_cl_if(dict,"log",CL_LOG,True,"site",loc);
     st->random=find_cl_if(dict,"random",CL_RANDOMSRC,True,"site",loc);
 
-    st->privkey=find_cl_if(dict,"local-key",CL_RSAPRIVKEY,True,"site",loc);
+    st->privkey=find_cl_if(dict,"local-key",CL_SIGPRIVKEY,True,"site",loc);
     st->addresses=dict_read_string_array(dict,"address",False,"site",loc,0);
     if (st->addresses)
        st->remoteport=dict_read_number(dict,"port",True,"site",loc,0);
     else st->remoteport=0;
-    st->pubkey=find_cl_if(dict,"key",CL_RSAPUBKEY,True,"site",loc);
+    st->pubkey=find_cl_if(dict,"key",CL_SIGPUBKEY,True,"site",loc);
 
     GET_CLOSURE_LIST("transform",transforms,ntransforms,CL_TRANSFORM);
 
     st->dh=find_cl_if(dict,"dh",CL_DH,True,"site",loc);
-    st->hash=find_cl_if(dict,"hash",CL_HASH,True,"site",loc);
+
+    if (st->privkey->sethash || st->pubkey->sethash) {
+       struct hash_if *hash=find_cl_if(dict,"hash",CL_HASH,True,"site",loc);
+       if (st->privkey->sethash) st->privkey->sethash(st->privkey->st,hash);
+       if (st->pubkey->sethash) st->pubkey->sethash(st->pubkey->st,hash);
+    }
 
 #define DEFAULT(D) (st->peer_mobile || st->local_mobile        \
                     ? DEFAULT_MOBILE_##D : DEFAULT_##D)
@@ -2104,7 +2196,7 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
     st->key_lifetime=         CFG_NUMBER("key-lifetime",  KEY_LIFETIME);
     st->setup_retries=        CFG_NUMBER("setup-retries", SETUP_RETRIES);
     st->setup_retry_interval= CFG_NUMBER("setup-timeout", SETUP_RETRY_INTERVAL);
-    st->wait_timeout=         CFG_NUMBER("wait-time",     WAIT_TIME);
+    st->wait_timeout_mean=    CFG_NUMBER("wait-time",     WAIT_TIME);
     st->mtu_target= dict_read_number(dict,"mtu-target",False,"site",loc,0);
 
     st->mobile_peer_expiry= dict_read_number(
@@ -2167,14 +2259,18 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
     st->sharedsecretlen=st->sharedsecretallocd=0;
     st->sharedsecret=0;
 
-    for (i=0; i<st->ntransforms; i++) {
-       struct transform_if *ti=st->transforms[i];
-       uint32_t capbit = 1UL << ti->capab_transformnum;
-       if (st->local_capabilities & capbit)
-           slog(st,LOG_ERROR,"transformnum capability bit"
-                " %d (%#"PRIx32") reused", ti->capab_transformnum, capbit);
-       st->local_capabilities |= capbit;
-    }
+#define SET_CAPBIT(bit) do {                                           \
+    uint32_t capflag = 1UL << (bit);                                   \
+    if (st->local_capabilities & capflag)                              \
+       slog(st,LOG_ERROR,"capability bit"                              \
+            " %d (%#"PRIx32") reused", (bit), capflag);                \
+    st->local_capabilities |= capflag;                                 \
+} while (0)
+
+    for (i=0; i<st->ntransforms; i++)
+       SET_CAPBIT(st->transforms[i]->capab_bit);
+
+#undef SET_CAPBIT
 
     if (st->local_mobile || st->peer_mobile)
        st->local_capabilities |= CAPAB_PRIORITY_MOBILE;