- /* ECDH or old-style key exchange? */
-
- if(experimental && OPTION_VERSION(from->options) >= 2) {
- /* Check if we already have an ECDSA public key for this node. */
-
- if(!node_read_ecdsa_public_key(from)) {
- logger(DEBUG_ALWAYS, LOG_ERR, "No ECDSA public key known for %s (%s), cannot verify ECDH key exchange!", from->name, from->hostname);
- return true;
- }
-
- int siglen = ecdsa_size(&from->ecdsa);
- int keylen = b64decode(key, key, sizeof key);
-
- if(keylen != ECDH_SIZE + siglen) {
- logger(DEBUG_ALWAYS, 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(DEBUG_ALWAYS, LOG_ERR, "ECDH key too short for cipher of %s!", from->name);
- return true;
- }
-
- if(!ecdsa_verify(&from->ecdsa, key, ECDH_SIZE, key + ECDH_SIZE)) {
- logger(DEBUG_ALWAYS, LOG_ERR, "Possible intruder %s (%s): %s", from->name, from->hostname, "invalid ECDSA signature");
- return true;
- }
-
- if(!from->ecdh) {
- if(!send_ans_key_ecdh(from))
- return true;
- }
-
- char shared[ECDH_SHARED_SIZE * 2 + 1];
-
- if(!ecdh_compute_shared(&from->ecdh, key, 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);