From: Ian Jackson Date: Sat, 7 Dec 2019 18:21:11 +0000 (+0000) Subject: site: Read public peer keys from key file X-Git-Tag: v0.6.0~139 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=secnet.git;a=commitdiff_plain;h=db10dcc854450232187cec829c60dac619344d50 site: Read public peer keys from key file Instead of a sigpubkey_if, we maintain a peer_keyset for the peer. We try to get this from the new "peer-keys" configuration key which specifies a file (to be parsed with the parser pubkeys.fl.pl). Failing that we fall back to the old "key" (for key id 0000000000). We actually keep up to two peer_keysets, because we don't want the set to change during the middle of a key exchange. (In the future, peer keysets might change during operation.) We make a "copy" (actually a reference) at the start of key exchange. We advertise the keyids we will accept in our MSG2/MSG3. We expect the peer to state in their MSG3/MSG4 which key they have actually signed the message with. This commit has a bug: it never calls sethash. Our only algorithm right now is rsa1 and needs sethash. So attempts to use this will segfault. Signed-off-by: Ian Jackson --- diff --git a/README b/README index 60b3407..05b073f 100644 --- a/README +++ b/README @@ -398,7 +398,9 @@ site: dict argument address literals are supported too if enclosed in `[' `]'. port (integer): mandatory if 'address' is specified: the port used to contact our peer - key (rsapubkey closure): our peer's public key + peer-keys (string): path (prefix) for peer public key set file(s); + see README.make-secnet-sites re `pub' etc. + key (rsapubkey closure): our peer's public key (obsolete) transform (transform closure): how to mangle packets sent between sites dh (dh closure) hash (hash closure) diff --git a/README.make-secnet-sites b/README.make-secnet-sites index 579b9a5..43816ce 100644 --- a/README.make-secnet-sites +++ b/README.make-secnet-sites @@ -187,6 +187,7 @@ INPUT SYNTAX site level, but optional. Properties which can also appear in public key files. + (named by `peer-keys' key to secnet sites closure.) These are acceptable to make-secnet-sites only at site level. See also `Site long-term keys' in NOTES. diff --git a/site.c b/site.c index 58db825..002b7dd 100644 --- a/site.c +++ b/site.c @@ -43,6 +43,7 @@ #include "util.h" #include "unaligned.h" #include "magic.h" +#include "pubkeys.h" #define SETUP_BUFFER_LEN 2048 @@ -318,7 +319,6 @@ struct site { struct random_if *random; struct privcache_if *privkeys; struct sigprivkey_if *privkey_fixed; - struct sigpubkey_if *pubkey; struct transform_if **transforms; int ntransforms; struct dh_if *dh; @@ -347,6 +347,8 @@ struct site { int resolving_n_results_all; int resolving_n_results_stored; struct comm_addr resolving_results[MAX_PEER_ADDRS]; + const char *peerkeys_path; + struct peer_keyset *peerkeys_current, *peerkeys_kex; /* The currently established session */ struct data_key current; @@ -559,6 +561,7 @@ struct msg { struct alg_msg_data sig; int n_pubkeys_accepted_nom; /* may be > MAX_SIG_KEYS ! */ const struct sigkeyid *pubkeys_accepted[MAX_SIG_KEYS]; + int signing_key_index; }; static const struct sigkeyid keyid_zero; @@ -664,13 +667,11 @@ static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what, buf_append_uint16(&st->buffer,st->mtu_target); } if (type_is_msg23(type)) { - /* The code to advertise a public key acceptance list will - * come in a moment. But right now we must add the byte - * indicating which key advertised by our peer we used, which - * comes after the public key acceptance list. So for now, - * explicitly advertise a list with just 0000000000. */ - buf_append_uint8(&st->buffer,1); - BUF_ADD_OBJ(append,&st->buffer,keyid_zero); + buf_append_uint8(&st->buffer,st->peerkeys_kex->nkeys); + for (ki=0; kipeerkeys_kex->nkeys; ki++) { + struct peer_pubkey *pk = &st->peerkeys_kex->keys[ki]; + BUF_ADD_OBJ(append,&st->buffer,pk->id); + } } struct sigprivkey_if *privkey=0; if (type_is_msg34(type)) { @@ -763,6 +764,7 @@ static bool_t unpick_msg(struct site *st, uint32_t type, m->n_pubkeys_accepted_nom=-1; m->capab_transformnum=-1; + m->signing_key_index=-1; m->hashstart=msg->start; CHECK_AVAIL(msg,4); m->dest=buf_unprepend_uint32(msg); @@ -792,6 +794,11 @@ static bool_t unpick_msg(struct site *st, uint32_t type, m->n_pubkeys_accepted_nom = 1; m->pubkeys_accepted[0] = &keyid_zero; } + if (type_is_msg34(type) && m->remote.extrainfo.size) { + m->signing_key_index=buf_unprepend_uint8(&m->remote.extrainfo); + } else { + m->signing_key_index=0; + } if (!unpick_name(msg,&m->local)) return False; if (type==LABEL_PROD) { CHECK_EMPTY(msg); @@ -827,8 +834,13 @@ static bool_t unpick_msg(struct site *st, uint32_t type, CHECK_AVAIL(msg,m->pklen); m->pk=buf_unprepend(msg,m->pklen); m->hashlen=msg->start-m->hashstart; - struct sigpubkey_if *pubkey=st->pubkey; + if (m->signing_key_index < 0 || + m->signing_key_index >= st->peerkeys_kex->nkeys) { + return False; + } + struct sigpubkey_if *pubkey= + st->peerkeys_kex->keys[m->signing_key_index].pubkey; if (!pubkey->unpick(pubkey->st,msg,&m->sig)) { return False; } @@ -884,6 +896,12 @@ static bool_t check_msg(struct site *st, uint32_t type, struct msg *m, static bool_t kex_init(struct site *st) { + keyset_dispose(&st->peerkeys_kex); + if (!st->peerkeys_current) { + slog(st,LOG_SETUP_INIT,"no peer public keys, abandoning key setup"); + return False; + } + st->peerkeys_kex = keyset_dup(st->peerkeys_current); st->random->generate(st->random->st,NONCELEN,st->localN); return True; } @@ -976,9 +994,25 @@ static bool_t generate_msg3(struct site *st, const struct msg *prompt) static bool_t process_msg3_msg4(struct site *st, struct msg *m) { - struct sigpubkey_if *pubkey=st->pubkey; - /* Check signature and store g^x mod m */ + int ki; + + if (m->signing_key_index >= 0) { + if (m->signing_key_index >= st->peerkeys_kex->nkeys) + return False; + ki=m->signing_key_index; + } else { + for (ki=0; kipeerkeys_kex->nkeys; ki++) + if (sigkeyid_equal(&keyid_zero,&st->peerkeys_kex->keys[ki].id)) + goto found; + /* not found */ + slog(st,LOG_ERROR, + "peer signed with keyid zero, which we do not accept"); + return False; + found:; + } + struct sigpubkey_if *pubkey=st->peerkeys_kex->keys[ki].pubkey; + if (!pubkey->check(pubkey->st, m->hashstart,m->hashlen, &m->sig)) { @@ -1609,6 +1643,7 @@ static void enter_state_run(struct site *st) st->setup_session_id=0; transport_peers_clear(st,&st->setup_peers); + keyset_dispose(&st->peerkeys_kex); FILLZERO(st->localN); FILLZERO(st->remoteN); dispose_transform(&st->new_transform); @@ -2233,6 +2268,8 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, st->ops.st=st; st->ops.control=site_control; st->ops.status=site_status; + st->peerkeys_path=0; + st->peerkeys_current=st->peerkeys_kex=0; /* First parameter must be a dict */ item=list_elem(args,0); @@ -2316,18 +2353,37 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, SETUP_SETHASH(st->privkey_fixed); } + struct sigpubkey_if *fixed_pubkey + =find_cl_if(dict,"key",CL_SIGPUBKEY,False,"site",loc); + st->peerkeys_path=dict_read_string(dict,"peer-keys",fixed_pubkey==0, + "site",loc); + if (st->peerkeys_path) { + st->peerkeys_current=keyset_load(st->peerkeys_path, + &st->scratch,st->log,M_ERR); + if (fixed_pubkey) { + fixed_pubkey->dispose(fixed_pubkey->st); + } + } else { + assert(fixed_pubkey); + SETUP_SETHASH(fixed_pubkey); + NEW(st->peerkeys_current); + st->peerkeys_current->refcount=1; + st->peerkeys_current->nkeys=1; + st->peerkeys_current->keys[0].id=keyid_zero; + st->peerkeys_current->keys[0].pubkey=fixed_pubkey; + slog(st,LOG_SIGKEYS, + "using old-style fixed peer public key (no `peer-keys')"); + } + 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_SIGPUBKEY,True,"site",loc); GET_CLOSURE_LIST("transform",transforms,ntransforms,CL_TRANSFORM); st->dh=find_cl_if(dict,"dh",CL_DH,True,"site",loc); - SETUP_SETHASH(st->pubkey); - #define DEFAULT(D) (st->peer_mobile || st->local_mobile \ ? DEFAULT_MOBILE_##D : DEFAULT_##D) #define CFG_NUMBER(k,D) dict_read_number(dict,(k),False,"site",loc,DEFAULT(D));