+ char *pubname = NULL;
+ char *p;
+
+ splay_tree_t config;
+ init_configuration(&config);
+
+ if(!read_host_config(&config, n->name, true)) {
+ goto exit;
+ }
+
+ /* First, check for simple Ed25519PublicKey statement */
+
+ if(get_config_string(lookup_config(&config, "Ed25519PublicKey"), &p)) {
+ n->ecdsa = ecdsa_set_base64_public_key(p);
+ free(p);
+ goto exit;
+ }
+
+ /* Else, check for Ed25519PublicKeyFile statement and read it */
+
+ if(!get_config_string(lookup_config(&config, "Ed25519PublicKeyFile"), &pubname)) {
+ xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, n->name);
+ }
+
+ fp = fopen(pubname, "r");
+
+ if(!fp) {
+ goto exit;
+ }
+
+ n->ecdsa = ecdsa_read_pem_public_key(fp);
+ fclose(fp);
+
+exit:
+ splay_empty_tree(&config);
+ free(pubname);
+ return n->ecdsa;
+}
+
+static bool read_invitation_key(void) {
+ FILE *fp;
+ char fname[PATH_MAX];
+
+ if(invitation_key) {
+ ecdsa_free(invitation_key);
+ invitation_key = NULL;
+ }
+
+ snprintf(fname, sizeof(fname), "%s" SLASH "invitations" SLASH "ed25519_key.priv", confbase);
+
+ fp = fopen(fname, "r");
+
+ if(fp) {
+ invitation_key = ecdsa_read_pem_private_key(fp);
+ fclose(fp);
+
+ if(!invitation_key) {
+ logger(DEBUG_ALWAYS, LOG_ERR, "Reading Ed25519 private key file `%s' failed", fname);
+ }
+ }
+
+ return invitation_key;
+}
+
+#ifndef DISABLE_LEGACY
+static timeout_t keyexpire_timeout;
+
+static void keyexpire_handler(void *data) {
+ regenerate_key();
+ timeout_set(data, &(struct timeval) {
+ keylifetime, jitter()
+ });
+}
+#endif
+
+void regenerate_key(void) {
+ logger(DEBUG_STATUS, LOG_INFO, "Expiring symmetric keys");
+ send_key_changed();
+
+ for splay_each(node_t, n, &node_tree) {
+ n->status.validkey_in = false;
+ }
+}
+
+void load_all_nodes(void) {
+ DIR *dir;
+ struct dirent *ent;
+ char dname[PATH_MAX];
+
+ snprintf(dname, sizeof(dname), "%s" SLASH "hosts", confbase);
+ dir = opendir(dname);