#define SITE_SENTMSG5 7
#define SITE_WAIT 8
+#define CASES_MSG3_KNOWN LABEL_MSG3: case LABEL_MSG3BIS: case LABEL_MSG3TER
+
int32_t site_max_start_pad = 4*4;
static cstring_t state_name(uint32_t state)
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 */
+ 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 */
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 */
uint64_t timeout; /* Timeout for current state */
uint8_t *dhsecret;
uint8_t *sharedsecret;
- uint32_t sharedsecretlen, sharedsecretallocd;
struct transform_inst_if *new_transform; /* For key setup/verify */
};
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 {
uint32_t remote_capabilities;
uint16_t remote_mtu;
int capab_transformnum;
+ int capab_dhnum;
uint8_t *nR;
uint8_t *nL;
int32_t pklen;
- char *pk;
+ uint8_t *pk;
int32_t hashlen;
int32_t siglen;
char *sig;
return t;
}
-static _Bool set_new_transform(struct site *st, char *pk)
+static _Bool set_new_transform(struct site *st, uint8_t *pk, int32_t pklen)
{
_Bool ok;
- /* Make room for the shared key */
- st->sharedsecretlen=st->chosen_transform->keylen?:st->dh->ceil_len;
- assert(st->sharedsecretlen);
- if (st->sharedsecretlen > st->sharedsecretallocd) {
- st->sharedsecretallocd=st->sharedsecretlen;
- st->sharedsecret=safe_realloc_ary(st->sharedsecret,1,
- st->sharedsecretallocd,
- "site:sharedsecret");
- }
-
/* Generate the shared key */
- st->dh->makeshared(st->dh->st,st->dhsecret,st->dh->len,pk,
- st->sharedsecret,st->sharedsecretlen);
+ assert(!st->sharedsecret);
+ st->sharedsecret = safe_malloc(st->chosen_dh->shared_len,
+ "site:sharedsecret");
+ pk[pklen]=0; /* clobbers the following signature length, which we've
+ * already copied */
+ if (!st->chosen_dh->makeshared(st->chosen_dh->st,
+ st->dhsecret,st->chosen_dh->secret_len,
+ pk,pklen,
+ 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->sharedsecretlen,st->our_name_later);
+ st->chosen_dh->shared_len,st->our_name_later);
dispose_transform(&st->new_transform);
if (!ok) return False;
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;
}
{
void *hst;
uint8_t *hash;
- string_t dhpub, sig;
+ string_t sig;
+ uint8_t *pklen_addr;
+ int32_t pklen;
+ void *pk;
+ unsigned minor;
st->retries=st->setup_retries;
BUF_ALLOC(&st->buffer,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)) {
if (hacky_par_mid_failnow()) return False;
- if (type==LABEL_MSG3BIS)
- buf_append_uint8(&st->buffer,st->chosen_transform->capab_transformnum);
-
- dhpub=st->dh->makepublic(st->dh->st,st->dhsecret,st->dh->len);
- buf_append_string(&st->buffer,dhpub);
- free(dhpub);
+ if (MSGMAJOR(type) == 3) do {
+ 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);
+
+ pklen_addr=buf_append(&st->buffer,2);
+ pk=buf_append(&st->buffer,st->chosen_dh->public_len);
+ pklen=st->chosen_dh->makepublic(st->chosen_dh->st,
+ pk,st->chosen_dh->public_len,
+ st->dhsecret,st->chosen_dh->secret_len);
+ put_uint16(pklen_addr,pklen);
+ buf_unappend(&st->buffer,st->chosen_dh->public_len-pklen);
hash=safe_malloc(st->hash->len, "generate_msg");
hst=st->hash->init();
st->hash->update(hst,st->buffer.start,st->buffer.size);
static bool_t unpick_msg(struct site *st, uint32_t type,
struct buffer_if *msg, struct msg *m)
{
- m->capab_transformnum=-1;
+ unsigned minor;
+
+ m->capab_transformnum=m->capab_dhnum=-1;
m->hashstart=msg->start;
CHECK_AVAIL(msg,4);
m->dest=buf_unprepend_uint32(msg);
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);
+ MAYBE_READ_CAP(2, dh, CAPAB_BIT_TRADZP);
+#undef MAYBE_READ_CAP
+ } while (0);
CHECK_AVAIL(msg,2);
m->pklen=buf_unprepend_uint16(msg);
CHECK_AVAIL(msg,m->pklen);
}
/* 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;
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_transforms = st->remote_capabilities & CAPAB_TRANSFORM_MASK;
- if (!remote_transforms)
+ 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_transforms = 1UL << CAPAB_TRANSFORMNUM_ANCIENT;
+ 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; \
+ 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");
+ CHOOSE_CRYPTO(dh, "Diffie--Hellman groups");
+
+#undef CHOOSE_CRYPTO
memcpy(st->remoteN,m.nR,NONCELEN);
return True;
}
+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->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)
{
/* Now we have our nonce and their nonce. Think of a secret key,
and create message number 3. */
- st->random->generate(st->random->st,st->dh->len,st->dhsecret);
+ generate_dhsecret(st);
return generate_msg(st,
- (st->remote_capabilities & CAPAB_TRANSFORM_MASK
- ? LABEL_MSG3BIS : LABEL_MSG3),
+ (st->remote_capabilities &
+ CAPAB_EXPLICIT_TRANSFORM_DH)
+ ? LABEL_MSG3TER
+ : (st->remote_capabilities &
+ CAPAB_INEXPLICIT_TRANSFORM_MASK)
+ ? LABEL_MSG3BIS
+ : LABEL_MSG3,
"site: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)) {
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")",
}
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");
+ CHOSE_CRYPTO(dh, "Diffie--Hellman group");
+
+#undef CHOSE_CRYPTO
if (!process_msg3_msg4(st,&m))
return False;
/* Terminate their DH public key with a '0' */
m.pk[m.pklen]=0;
/* Invent our DH secret key */
- st->random->generate(st->random->st,st->dh->len,st->dhsecret);
+ generate_dhsecret(st);
/* Generate the shared key and set up the transform */
- if (!set_new_transform(st,m.pk)) return False;
+ if (!set_new_transform(st,m.pk,m.pklen)) return False;
return True;
}
m.pk[m.pklen]=0;
/* Generate the shared key and set up the transform */
- if (!set_new_transform(st,m.pk)) return False;
+ if (!set_new_transform(st,m.pk,m.pklen)) return False;
return True;
}
FILLZERO(st->localN);
FILLZERO(st->remoteN);
dispose_transform(&st->new_transform);
- memset(st->dhsecret,0,st->dh->len);
- if (st->sharedsecret) memset(st->sharedsecret,0,st->sharedsecretlen);
+ if (st->dhsecret) {
+ memset(st->dhsecret, 0, st->chosen_dh->secret_len);
+ free(st->dhsecret);
+ st->dhsecret = 0;
+ }
+ if (st->sharedsecret) {
+ memset(st->sharedsecret, 0, st->chosen_dh->shared_len);
+ free(st->sharedsecret);
+ st->sharedsecret = 0;
+ }
set_link_quality(st);
if (st->keepalive && !current_valid(st))
}
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;
}
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) &&
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);
#define GET_CLOSURE_LIST(dictkey,things,nthings,CL_TYPE) do{ \
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 \
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);
transport_peers_clear(st,&st->setup_peers);
- /* XXX mlock these */
- st->dhsecret=safe_malloc(st->dh->len,"site:dhsecret");
- st->sharedsecretlen=st->sharedsecretallocd=0;
+ st->dhsecret=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);
+ for (i=0; i<st->ndhs; i++)
+ SET_CAPBIT(st->dhs[i]->capab_bit);
+
+#undef SET_CAPBIT
if (st->local_mobile || st->peer_mobile)
st->local_capabilities |= CAPAB_PRIORITY_MOBILE;