Very primitive ECDSA signed ECDH key exchange for the meta protocol.
Nonces and hash of the ID requests should be included in the seed for the PRF.
This commit is contained in:
parent
210b5ceeee
commit
8132be8fbd
2 changed files with 127 additions and 9 deletions
|
@ -24,7 +24,7 @@
|
||||||
/* Protocol version. Different major versions are incompatible. */
|
/* Protocol version. Different major versions are incompatible. */
|
||||||
|
|
||||||
#define PROT_MAJOR 17
|
#define PROT_MAJOR 17
|
||||||
#define PROT_MINOR 1
|
#define PROT_MINOR 2
|
||||||
|
|
||||||
/* Silly Windows */
|
/* Silly Windows */
|
||||||
|
|
||||||
|
@ -83,6 +83,7 @@ extern bool seen_request(char *);
|
||||||
|
|
||||||
extern bool send_id(struct connection_t *);
|
extern bool send_id(struct connection_t *);
|
||||||
extern bool send_metakey(struct connection_t *);
|
extern bool send_metakey(struct connection_t *);
|
||||||
|
extern bool send_metakey_ec(struct connection_t *);
|
||||||
extern bool send_challenge(struct connection_t *);
|
extern bool send_challenge(struct connection_t *);
|
||||||
extern bool send_chal_reply(struct connection_t *);
|
extern bool send_chal_reply(struct connection_t *);
|
||||||
extern bool send_ack(struct connection_t *);
|
extern bool send_ack(struct connection_t *);
|
||||||
|
|
|
@ -25,13 +25,16 @@
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
#include "control.h"
|
#include "control.h"
|
||||||
#include "control_common.h"
|
#include "control_common.h"
|
||||||
|
#include "cipher.h"
|
||||||
#include "crypto.h"
|
#include "crypto.h"
|
||||||
|
#include "digest.h"
|
||||||
#include "edge.h"
|
#include "edge.h"
|
||||||
#include "graph.h"
|
#include "graph.h"
|
||||||
#include "logger.h"
|
#include "logger.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
#include "netutl.h"
|
#include "netutl.h"
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
|
#include "prf.h"
|
||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
#include "rsa.h"
|
#include "rsa.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
@ -109,13 +112,40 @@ bool id_h(connection_t *c, char *request) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!read_rsa_public_key(c)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
c->allow_request = METAKEY;
|
c->allow_request = METAKEY;
|
||||||
|
|
||||||
return send_metakey(c);
|
if(c->protocol_minor >= 2)
|
||||||
|
return send_metakey_ec(c);
|
||||||
|
else
|
||||||
|
return send_metakey(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool send_metakey_ec(connection_t *c) {
|
||||||
|
if(!read_ecdsa_public_key(c))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
logger(LOG_DEBUG, "Sending ECDH metakey to %s", c->name);
|
||||||
|
|
||||||
|
size_t siglen = ecdsa_size(&myself->connection->ecdsa);
|
||||||
|
|
||||||
|
char key[ECDH_SIZE];
|
||||||
|
char sig[siglen];
|
||||||
|
|
||||||
|
// TODO: include nonce? Use relevant parts of SSH or TLS protocol
|
||||||
|
|
||||||
|
if(!ecdh_generate_public(&c->ecdh, key))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(!ecdsa_sign(&myself->connection->ecdsa, key, ECDH_SIZE, sig))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
char out[MAX_STRING_SIZE];
|
||||||
|
|
||||||
|
bin2hex(key, out, ECDH_SIZE);
|
||||||
|
bin2hex(sig, out + ECDH_SIZE * 2, siglen);
|
||||||
|
out[(ECDH_SIZE + siglen) * 2] = 0;
|
||||||
|
|
||||||
|
bool result = send_request(c, "%d %s", METAKEY, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool send_metakey(connection_t *c) {
|
bool send_metakey(connection_t *c) {
|
||||||
|
@ -124,6 +154,9 @@ bool send_metakey(connection_t *c) {
|
||||||
char enckey[len];
|
char enckey[len];
|
||||||
char hexkey[2 * len + 1];
|
char hexkey[2 * len + 1];
|
||||||
|
|
||||||
|
if(!read_rsa_public_key(c))
|
||||||
|
return false;
|
||||||
|
|
||||||
if(!cipher_open_blowfish_ofb(&c->outcipher))
|
if(!cipher_open_blowfish_ofb(&c->outcipher))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -182,7 +215,91 @@ bool send_metakey(connection_t *c) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool metakey_ec_h(connection_t *c, const char *request) {
|
||||||
|
size_t siglen = ecdsa_size(&c->ecdsa);
|
||||||
|
char in[MAX_STRING_SIZE];
|
||||||
|
char key[MAX_STRING_SIZE];
|
||||||
|
char sig[siglen];
|
||||||
|
|
||||||
|
logger(LOG_DEBUG, "Got ECDH metakey from %s", c->name);
|
||||||
|
|
||||||
|
if(sscanf(request, "%*d " MAX_STRING, in) != 1) {
|
||||||
|
logger(LOG_ERR, "Got bad %s from %s (%s)", "METAKEY", c->name, c->hostname);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(strlen(in) != (ECDH_SIZE + siglen) * 2) {
|
||||||
|
logger(LOG_ERR, "Possible intruder %s (%s): %s %d != %d", c->name, c->hostname, "wrong keylength", strlen(in) / 2, (ECDH_SIZE + siglen));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hex2bin(in, key, ECDH_SIZE);
|
||||||
|
hex2bin(in + ECDH_SIZE * 2, sig, siglen);
|
||||||
|
|
||||||
|
if(!ecdsa_verify(&c->ecdsa, key, ECDH_SIZE, sig)) {
|
||||||
|
logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "invalid ECDSA signature");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char shared[ECDH_SHARED_SIZE * 2 + 1];
|
||||||
|
|
||||||
|
if(!ecdh_compute_shared(&c->ecdh, key, shared))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Update our crypto end */
|
||||||
|
|
||||||
|
if(!cipher_open_by_name(&c->incipher, "aes-256-ofb"))
|
||||||
|
return false;
|
||||||
|
if(!digest_open_by_name(&c->indigest, "sha512", -1))
|
||||||
|
return false;
|
||||||
|
if(!cipher_open_by_name(&c->outcipher, "aes-256-ofb"))
|
||||||
|
return false;
|
||||||
|
if(!digest_open_by_name(&c->outdigest, "sha512", -1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
size_t mykeylen = cipher_keylength(&c->incipher);
|
||||||
|
size_t hiskeylen = cipher_keylength(&c->outcipher);
|
||||||
|
|
||||||
|
char *mykey;
|
||||||
|
char *hiskey;
|
||||||
|
char *seed;
|
||||||
|
|
||||||
|
if(strcmp(myself->name, c->name) < 0) {
|
||||||
|
mykey = key;
|
||||||
|
hiskey = key + mykeylen * 2;
|
||||||
|
xasprintf(&seed, "tinc TCP key expansion %s %s", myself->name, c->name);
|
||||||
|
} else {
|
||||||
|
mykey = key + hiskeylen * 2;
|
||||||
|
hiskey = key;
|
||||||
|
xasprintf(&seed, "tinc TCP key expansion %s %s", c->name, myself->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!prf(shared, ECDH_SHARED_SIZE, seed, strlen(seed), key, hiskeylen * 2 + mykeylen * 2))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
free(seed);
|
||||||
|
|
||||||
|
bin2hex(shared, shared, ECDH_SHARED_SIZE);
|
||||||
|
shared[ECDH_SHARED_SIZE * 2] = 0;
|
||||||
|
logger(LOG_DEBUG, "Shared secret is %s", shared);
|
||||||
|
|
||||||
|
cipher_set_key(&c->incipher, mykey, true);
|
||||||
|
digest_set_key(&c->indigest, mykey + mykeylen, mykeylen);
|
||||||
|
|
||||||
|
cipher_set_key(&c->outcipher, hiskey, false);
|
||||||
|
digest_set_key(&c->outdigest, hiskey + hiskeylen, hiskeylen);
|
||||||
|
|
||||||
|
c->status.decryptin = true;
|
||||||
|
c->status.encryptout = true;
|
||||||
|
c->allow_request = CHALLENGE;
|
||||||
|
|
||||||
|
return send_challenge(c);
|
||||||
|
}
|
||||||
|
|
||||||
bool metakey_h(connection_t *c, char *request) {
|
bool metakey_h(connection_t *c, char *request) {
|
||||||
|
if(c->protocol_minor >= 2)
|
||||||
|
return metakey_ec_h(c, request);
|
||||||
|
|
||||||
char hexkey[MAX_STRING_SIZE];
|
char hexkey[MAX_STRING_SIZE];
|
||||||
int cipher, digest, maclength, compression;
|
int cipher, digest, maclength, compression;
|
||||||
size_t len = rsa_size(&myself->connection->rsa);
|
size_t len = rsa_size(&myself->connection->rsa);
|
||||||
|
@ -238,7 +355,7 @@ bool metakey_h(connection_t *c, char *request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool send_challenge(connection_t *c) {
|
bool send_challenge(connection_t *c) {
|
||||||
size_t len = rsa_size(&c->rsa);
|
size_t len = c->protocol_minor >= 2 ? ECDH_SIZE : rsa_size(&c->rsa);
|
||||||
char buffer[len * 2 + 1];
|
char buffer[len * 2 + 1];
|
||||||
|
|
||||||
if(!c->hischallenge)
|
if(!c->hischallenge)
|
||||||
|
@ -260,7 +377,7 @@ bool send_challenge(connection_t *c) {
|
||||||
|
|
||||||
bool challenge_h(connection_t *c, char *request) {
|
bool challenge_h(connection_t *c, char *request) {
|
||||||
char buffer[MAX_STRING_SIZE];
|
char buffer[MAX_STRING_SIZE];
|
||||||
size_t len = rsa_size(&myself->connection->rsa);
|
size_t len = c->protocol_minor >= 2 ? ECDH_SIZE : rsa_size(&myself->connection->rsa);
|
||||||
size_t digestlen = digest_length(&c->indigest);
|
size_t digestlen = digest_length(&c->indigest);
|
||||||
char digest[digestlen];
|
char digest[digestlen];
|
||||||
|
|
||||||
|
@ -318,7 +435,7 @@ bool chal_reply_h(connection_t *c, char *request) {
|
||||||
|
|
||||||
/* Verify the hash */
|
/* Verify the hash */
|
||||||
|
|
||||||
if(!digest_verify(&c->outdigest, c->hischallenge, rsa_size(&c->rsa), hishash)) {
|
if(!digest_verify(&c->outdigest, c->hischallenge, c->protocol_minor >= 2 ? ECDH_SIZE : rsa_size(&c->rsa), hishash)) {
|
||||||
logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge reply");
|
logger(LOG_ERR, "Possible intruder %s (%s): %s", c->name, c->hostname, "wrong challenge reply");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue