+
+ from->outcompression = compression;
+
+ /* ECDH or old-style key exchange? */
+
+ if(experimental && !strncmp(key, "ECDH:", 5)) {
+ char *pubkey = strchr(key + 5, ':');
+ if(pubkey)
+ *pubkey++ = 0;
+
+ /* Check if we already have an ECDSA public key for this node.
+ * If not, use the one from the key exchange, and store it. */
+
+ if(!node_read_ecdsa_public_key(from)) {
+ if(!pubkey) {
+ logger(LOG_ERR, "No ECDSA public key known for %s (%s), cannot verify ECDH key exchange!", from->name, from->hostname);
+ return true;
+ }
+
+ if(!ecdsa_set_base64_public_key(&from->ecdsa, pubkey))
+ return true;
+
+ append_config_file(from->name, "ECDSAPublicKey", pubkey);
+ }
+
+ int siglen = ecdsa_size(&from->ecdsa);
+ int keylen = b64decode(key + 5, key + 5, sizeof key - 5);
+
+ if(keylen != ECDH_SIZE + siglen) {
+ logger(LOG_ERR, "Node %s (%s) uses wrong keylength! %d != %d", from->name, from->hostname, keylen, ECDH_SIZE + siglen);
+ return true;
+ }
+
+ if(ECDH_SHARED_SIZE < cipher_keylength(&from->outcipher)) {
+ logger(LOG_ERR, "ECDH key too short for cipher of %s!", from->name);
+ return true;
+ }
+
+ if(!ecdsa_verify(&from->ecdsa, key + 5, ECDH_SIZE, key + 5 + ECDH_SIZE)) {
+ logger(LOG_ERR, "Possible intruder %s (%s): %s", from->name, from->hostname, "invalid ECDSA signature");
+ return true;
+ }
+
+ if(!from->ecdh) {
+ from->status.ecdh = true;
+ if(!send_ans_key(from))
+ return true;
+ }
+
+ char shared[ECDH_SHARED_SIZE * 2 + 1];
+
+ if(!ecdh_compute_shared(&from->ecdh, key + 5, shared))
+ return true;
+
+ /* Update our crypto end */
+
+ size_t mykeylen = cipher_keylength(&myself->incipher);
+ size_t hiskeylen = cipher_keylength(&from->outcipher);
+
+ char *mykey;
+ char *hiskey;
+ char *seed;
+
+ if(strcmp(myself->name, from->name) < 0) {
+ mykey = key;
+ hiskey = key + mykeylen * 2;
+ xasprintf(&seed, "tinc UDP key expansion %s %s", myself->name, from->name);
+ } else {
+ mykey = key + hiskeylen * 2;
+ hiskey = key;
+ xasprintf(&seed, "tinc UDP key expansion %s %s", from->name, myself->name);
+ }
+
+ if(!prf(shared, ECDH_SHARED_SIZE, seed, strlen(seed), key, hiskeylen * 2 + mykeylen * 2))
+ return true;
+
+ free(seed);
+
+ cipher_open_by_nid(&from->incipher, cipher_get_nid(&myself->incipher));
+ digest_open_by_nid(&from->indigest, digest_get_nid(&myself->indigest), digest_length(&myself->indigest));
+ from->incompression = myself->incompression;
+
+ cipher_set_key(&from->incipher, mykey, false);
+ digest_set_key(&from->indigest, mykey + mykeylen, mykeylen);
+
+ cipher_set_key(&from->outcipher, hiskey, true);
+ digest_set_key(&from->outdigest, hiskey + hiskeylen, hiskeylen);
+
+ // Reset sequence number and late packet window
+ mykeyused = true;
+ from->received_seqno = 0;
+ if(replaywin)
+ memset(from->late, 0, replaywin);
+
+ if(strcmp(myself->name, from->name) < 0)
+ memmove(key, key + mykeylen * 2, hiskeylen * 2);
+ } else {
+ keylen = hex2bin(key, key, sizeof key);
+
+ if(keylen != cipher_keylength(&from->outcipher)) {
+ logger(LOG_ERR, "Node %s (%s) uses wrong keylength!", from->name, from->hostname);
+ return true;
+ }
+
+ /* Update our copy of the origin's packet key */
+
+ cipher_set_key(&from->outcipher, key, true);
+ digest_set_key(&from->outdigest, key, keylen);
+ }
+
+ from->status.validkey = true;
+ from->sent_seqno = 0;
+
+ if(*address && *port) {
+ ifdebug(PROTOCOL) logger(LOG_DEBUG, "Using reflexive UDP address from %s: %s port %s", from->name, address, port);
+ sockaddr_t sa = str2sockaddr(address, port);
+ update_node_udp(from, &sa);
+ }
+
+ if(from->options & OPTION_PMTU_DISCOVERY)
+ send_mtu_probe(from);
+
+ return true;