Introduce negotiation for Diffie--Hellman groups.
[secnet] / site.c
diff --git a/site.c b/site.c
index 4f954e6..4da99e3 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
+#define CASES_MSG3_KNOWN LABEL_MSG3: case LABEL_MSG3BIS: case LABEL_MSG3TER
 
 int32_t site_max_start_pad = 4*4;
 
@@ -316,7 +316,8 @@ struct site {
     struct rsapubkey_if *pubkey;
     struct transform_if **transforms;
     int ntransforms;
-    struct dh_if *dh;
+    struct dh_if **dhs;
+    int ndhs;
     struct hash_if *hash;
 
     uint32_t index; /* Index of this site */
@@ -361,6 +362,7 @@ struct site {
     uint32_t remote_capabilities;
     uint16_t remote_adv_mtu;
     struct transform_if *chosen_transform;
+    struct dh_if *chosen_dh;
     uint32_t setup_session_id;
     transport_peers setup_peers;
     uint8_t localN[NONCELEN]; /* Nonces for key exchange */
@@ -528,6 +530,7 @@ struct msg {
     uint32_t remote_capabilities;
     uint16_t remote_mtu;
     int capab_transformnum;
+    int capab_dhnum;
     uint8_t *nR;
     uint8_t *nL;
     int32_t pklen;
@@ -553,16 +556,20 @@ static _Bool set_new_transform(struct site *st, char *pk)
 
     /* Generate the shared key */
     assert(!st->sharedsecret);
-    st->sharedsecret = safe_malloc(st->dh->shared_len, "site:sharedsecret");
-    if (!st->dh->makeshared(st->dh->st,st->dhsecret,st->dh->secret_len,
-                           pk, st->sharedsecret,st->dh->shared_len))
+    st->sharedsecret = safe_malloc(st->chosen_dh->shared_len,
+                                  "site:sharedsecret");
+    if (!st->chosen_dh->makeshared(st->chosen_dh->st,
+                                  st->dhsecret,st->chosen_dh->secret_len,
+                                  pk,
+                                  st->sharedsecret,
+                                  st->chosen_dh->shared_len))
        return False;
 
     /* Set up the transform */
     struct transform_if *generator=st->chosen_transform;
     struct transform_inst_if *generated=generator->create(generator->st);
     ok = generated->setkey(generated->st,st->sharedsecret,
-                          st->dh->shared_len,st->our_name_later);
+                          st->chosen_dh->shared_len,st->our_name_later);
 
     dispose_transform(&st->new_transform);
     if (!ok) return False;
@@ -643,9 +650,12 @@ static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what)
        minor = MSGMINOR(type);
        if (minor < 1) break;
        buf_append_uint8(&st->buffer,st->chosen_transform->capab_bit);
+       if (minor < 2) break;
+       buf_append_uint8(&st->buffer,st->chosen_dh->capab_bit);
     } while (0);
 
-    dhpub=st->dh->makepublic(st->dh->st,st->dhsecret,st->dh->secret_len);
+    dhpub=st->chosen_dh->makepublic(st->chosen_dh->st,
+                                   st->dhsecret,st->chosen_dh->secret_len);
     buf_append_string(&st->buffer,dhpub);
     free(dhpub);
     hash=safe_malloc(st->hash->len, "generate_msg");
@@ -680,7 +690,7 @@ static bool_t unpick_msg(struct site *st, uint32_t type,
 {
     unsigned minor;
 
-    m->capab_transformnum=-1;
+    m->capab_transformnum=m->capab_dhnum=-1;
     m->hashstart=msg->start;
     CHECK_AVAIL(msg,4);
     m->dest=buf_unprepend_uint32(msg);
@@ -726,6 +736,7 @@ static bool_t unpick_msg(struct site *st, uint32_t type,
     }                                                                  \
 } while (0)
        MAYBE_READ_CAP(1, transform, CAPAB_BIT_ANCIENTTRANSFORM);
+       MAYBE_READ_CAP(2, dh, CAPAB_BIT_TRADZP);
 #undef MAYBE_READ_CAP
     } while (0);
     CHECK_AVAIL(msg,2);
@@ -831,12 +842,17 @@ static bool_t process_msg2(struct site *st, struct buffer_if *msg2,
     st->setup_session_id=m.source;
     st->remote_capabilities=m.remote_capabilities;
 
-    /* Select the transform to use */
+    /* Select the transform and DH group to use */
 
-    uint32_t remote_crypto_caps = st->remote_capabilities & CAPAB_TRANSFORM_MASK;
+    uint32_t remote_crypto_caps = st->remote_capabilities;
+    if (!(remote_crypto_caps & CAPAB_EXPLICIT_TRANSFORM_DH))
+       remote_crypto_caps &= CAPAB_INEXPLICIT_TRANSFORM_MASK;
     if (!remote_crypto_caps)
        /* old secnets only had this one transform */
        remote_crypto_caps = 1UL << CAPAB_BIT_ANCIENTTRANSFORM;
+    if (!(remote_crypto_caps & CAPAB_EXPLICIT_TRANSFORM_DH))
+       /* old secnets only had this one kind of group */
+       remote_crypto_caps |= 1UL << CAPAB_BIT_TRADZP;
 
 #define CHOOSE_CRYPTO(kind, whats) do {                                        \
     struct kind##_if *iface;                                           \
@@ -857,6 +873,7 @@ kind##_found:                                                               \
 } while (0)
 
     CHOOSE_CRYPTO(transform, "transforms");
+    CHOOSE_CRYPTO(dh, "Diffie--Hellman groups");
 
 #undef CHOOSE_CRYPTO
 
@@ -866,9 +883,14 @@ kind##_found:                                                              \
 
 static void generate_dhsecret(struct site *st)
 {
+    slog(st,LOG_SETUP_INIT,"key exchange negotiated DH group"
+        " %d (capabilities ours=%#"PRIx32" theirs=%#"PRIx32")",
+        st->chosen_dh->capab_bit,
+        st->local_capabilities, st->remote_capabilities);
     assert(!st->dhsecret);
-    st->dhsecret = safe_malloc(st->dh->secret_len, "site:dhsecret");
-    st->random->generate(st->random->st, st->dh->secret_len,st->dhsecret);
+    st->dhsecret = safe_malloc(st->chosen_dh->secret_len, "site:dhsecret");
+    st->random->generate(st->random->st,
+                        st->chosen_dh->secret_len,st->dhsecret);
 }
 
 static bool_t generate_msg3(struct site *st)
@@ -877,7 +899,11 @@ static bool_t generate_msg3(struct site *st)
        and create message number 3. */
     generate_dhsecret(st);
     return generate_msg(st,
-                       (st->remote_capabilities & CAPAB_TRANSFORM_MASK)
+                       (st->remote_capabilities &
+                          CAPAB_EXPLICIT_TRANSFORM_DH)
+                       ? LABEL_MSG3TER
+                       : (st->remote_capabilities &
+                            CAPAB_INEXPLICIT_TRANSFORM_MASK)
                        ? LABEL_MSG3BIS
                        : LABEL_MSG3,
                        "site:MSG3");
@@ -949,6 +975,7 @@ kind##_found:                                                               \
 } while (0)
 
     CHOSE_CRYPTO(transform, "transform");
+    CHOSE_CRYPTO(dh, "Diffie--Hellman group");
 
 #undef CHOSE_CRYPTO
 
@@ -1520,12 +1547,12 @@ static void enter_state_run(struct site *st)
     FILLZERO(st->remoteN);
     dispose_transform(&st->new_transform);
     if (st->dhsecret) {
-       memset(st->dhsecret, 0, st->dh->secret_len);
+       memset(st->dhsecret, 0, st->chosen_dh->secret_len);
        free(st->dhsecret);
        st->dhsecret = 0;
     }
     if (st->sharedsecret) {
-       memset(st->sharedsecret, 0, st->dh->shared_len);
+       memset(st->sharedsecret, 0, st->chosen_dh->shared_len);
        free(st->sharedsecret);
        st->sharedsecret = 0;
     }
@@ -2151,7 +2178,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->local_capabilities = CAPAB_EXPLICIT_TRANSFORM_DH;
     st->early_capabilities = CAPAB_PRIORITY_MOBILE;
     st->netlink=find_cl_if(dict,"link",CL_NETLINK,True,"site",loc);
 
@@ -2195,8 +2222,8 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
     st->pubkey=find_cl_if(dict,"key",CL_RSAPUBKEY,True,"site",loc);
 
     GET_CLOSURE_LIST("transform",transforms,ntransforms,CL_TRANSFORM);
+    GET_CLOSURE_LIST("dh",dhs,ndhs,CL_DH);
 
-    st->dh=find_cl_if(dict,"dh",CL_DH,True,"site",loc);
     st->hash=find_cl_if(dict,"hash",CL_HASH,True,"site",loc);
 
 #define DEFAULT(D) (st->peer_mobile || st->local_mobile        \
@@ -2260,6 +2287,7 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
 
     st->remote_capabilities=0;
     st->chosen_transform=0;
+    st->chosen_dh=0;
     st->current.key_timeout=0;
     st->auxiliary_key.key_timeout=0;
     transport_peers_clear(st,&st->peers);
@@ -2277,6 +2305,8 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
 
     for (i=0; i<st->ntransforms; i++)
        SET_CAPBIT(st->transforms[i]->capab_bit);
+    for (i=0; i<st->ndhs; i++)
+       SET_CAPBIT(st->dhs[i]->capab_bit);
 
 #undef SET_CAPBIT