Implement a key exchange mechanism
This commit is contained in:
parent
1159f54fe4
commit
df8bacead5
11
README.md
11
README.md
@ -8,10 +8,11 @@ compiled until version 1.0.4 is out).
|
|||||||
* Unauthenticated encryption schemes were replaced with aesni and
|
* Unauthenticated encryption schemes were replaced with aesni and
|
||||||
pclmulqdq-accelerated AES256-GCM.
|
pclmulqdq-accelerated AES256-GCM.
|
||||||
|
|
||||||
|
* The static, shared key was replaced by an ephemeral keys exchange with
|
||||||
|
Curve25519. The PSK is now only used to sign ephemeral public keys and
|
||||||
|
parameters.
|
||||||
|
|
||||||
* Protection against replay attacks was added.
|
* Protection against replay attacks was added.
|
||||||
|
|
||||||
* More secure key derivation and initial handshake.
|
* Passwords are not kept in memory, guarded memory allocations are
|
||||||
|
used for secrets.
|
||||||
* Passwords are not kept in memory.
|
|
||||||
|
|
||||||
* Guarded memory allocations for secrets.
|
|
||||||
|
412
auth.c
412
auth.c
@ -1,28 +1,23 @@
|
|||||||
/*
|
/*
|
||||||
VTun - Virtual Tunnel over TCP/IP network.
|
* Key exchange
|
||||||
|
*
|
||||||
Copyright (C) 1998-2008 Maxim Krasnyansky <max_mk@yahoo.com>
|
* Client <-> Server
|
||||||
|
*
|
||||||
VTun has been derived from VPPP package by Maxim Krasnyansky.
|
* -> "CKEY " || host || " " || ts || Cpk || Hk(ts || Cpk)
|
||||||
|
* <- "SKEY " || Spk || Hk(Spk || Hk(ts || Cpk))
|
||||||
This program is free software; you can redistribute it and/or modify
|
* -> "CACK " || Hk("CACK" || Hk(Spk || Hk(ts || Cpk)))
|
||||||
it under the terms of the GNU General Public License as published by
|
* <- "FLAGS " || flags || " " || Hk(flags || Hk("CACK" || Hk(Spk || Hk(ts || Cpk))))
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
*
|
||||||
(at your option) any later version.
|
* session_key = Hk(DH)
|
||||||
|
*
|
||||||
This program is distributed in the hope that it will be useful,
|
* ts: current timestamp, 4 big-endian bytes
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* (Cpk, Csk): client public/secret ephemeral key pair
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* (Spk, Ssk): server public/secret ephemeral key pair
|
||||||
GNU General Public License for more details.
|
* Hk: keyed hash function, key derived from the PSK
|
||||||
*/
|
*
|
||||||
|
* DH function: Curve25519
|
||||||
/*
|
* Keyed hash function: Blake2b
|
||||||
* $Id: auth.c,v 1.9.2.5 2013/07/07 19:54:20 mtbishop Exp $
|
* KDF: Scrypt
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Challenge based authentication.
|
|
||||||
* Thanx to Chris Todd<christ@insynq.com> for the good idea.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
@ -51,62 +46,40 @@
|
|||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <sodium.h>
|
||||||
|
|
||||||
#include "vtun.h"
|
#include "vtun.h"
|
||||||
#include "lib.h"
|
#include "lib.h"
|
||||||
#include "lock.h"
|
#include "lock.h"
|
||||||
#include "auth.h"
|
#include "auth.h"
|
||||||
|
|
||||||
/* Encryption and Decryption of the challenge key */
|
|
||||||
#include <sodium.h>
|
|
||||||
|
|
||||||
static int derive_key(struct vtun_host *host)
|
static int derive_key(struct vtun_host *host)
|
||||||
{
|
{
|
||||||
unsigned char salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES];
|
unsigned char salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES];
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
size_t bin_len;
|
size_t bin_len;
|
||||||
|
|
||||||
if (host->key != NULL) {
|
if (host->akey != NULL) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if ((host->key = sodium_malloc(HOST_KEYBYTES)) == NULL) {
|
if ((host->akey = sodium_malloc(crypto_generichash_KEYBYTES)) == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
sodium_hex2bin(host->key, HOST_KEYBYTES, host->passwd,
|
|
||||||
strlen(host->passwd), "", &bin_len, NULL);
|
|
||||||
if (bin_len == HOST_KEYBYTES) {
|
|
||||||
vtun_syslog(LOG_ERR,"supplied password is long enough to be the secret");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(salt, 0xd1, sizeof salt);
|
memset(salt, 0xd1, sizeof salt);
|
||||||
if (crypto_pwhash_scryptsalsa208sha256
|
if (crypto_pwhash_scryptsalsa208sha256(host->akey, HOST_KEYBYTES,
|
||||||
(host->key, HOST_KEYBYTES, host->passwd, strlen(host->passwd), salt,
|
host->passwd, strlen(host->passwd), salt,
|
||||||
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE,
|
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE,
|
||||||
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) == 0) {
|
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) == 0) {
|
||||||
ret = 0;
|
ret = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
sodium_memzero(host->passwd, strlen(host->passwd));
|
sodium_memzero(host->passwd, strlen(host->passwd));
|
||||||
free(host->passwd);
|
free(host->passwd);
|
||||||
host->passwd = NULL;
|
host->passwd = NULL;
|
||||||
vtun_syslog(LOG_DEBUG,"Key ready for host %s.", host->host);
|
vtun_syslog(LOG_DEBUG, "Key ready for host [%s]", host->host);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gen_chal(char *chal)
|
|
||||||
{
|
|
||||||
randombytes_buf((unsigned char *) chal, VTUN_CHAL_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void auth_chal(char *chal, const struct vtun_host *host)
|
|
||||||
{
|
|
||||||
crypto_generichash((unsigned char *) chal, VTUN_CHAL_SIZE,
|
|
||||||
(const unsigned char *) chal,
|
|
||||||
VTUN_CHAL_SIZE, host->key, HOST_KEYBYTES);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Functions to convert binary flags to character string.
|
* Functions to convert binary flags to character string.
|
||||||
* string format: <CS64>
|
* string format: <CS64>
|
||||||
@ -128,25 +101,20 @@ static char *bf2cf(struct vtun_host *host)
|
|||||||
*(ptr++) = 'U';
|
*(ptr++) = 'U';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (host->flags & VTUN_TYPE_MASK) {
|
switch (host->flags & VTUN_TYPE_MASK) {
|
||||||
case VTUN_TTY:
|
case VTUN_TTY:
|
||||||
*(ptr++) = 't';
|
*(ptr++) = 't';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VTUN_PIPE:
|
case VTUN_PIPE:
|
||||||
*(ptr++) = 'p';
|
*(ptr++) = 'p';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VTUN_ETHER:
|
case VTUN_ETHER:
|
||||||
*(ptr++) = 'e';
|
*(ptr++) = 'e';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VTUN_TUN:
|
case VTUN_TUN:
|
||||||
*(ptr++) = 'u';
|
*(ptr++) = 'u';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((host->flags & VTUN_SHAPE) /* && host->spd_in */)
|
if ((host->flags & VTUN_SHAPE) /* && host->spd_in */)
|
||||||
ptr += sprintf(ptr, "S%d", host->spd_in);
|
ptr += sprintf(ptr, "S%d", host->spd_in);
|
||||||
|
|
||||||
@ -168,16 +136,18 @@ static char *bf2cf(struct vtun_host *host)
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* return 1 on success, otherwise 0
|
/*
|
||||||
Example:
|
* return 0 on success, otherwise -1
|
||||||
FLAGS: <TuE1>
|
* Example: FLAGS: <TuE1>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int cf2bf(char *str, struct vtun_host *host)
|
static int cf2bf(char *str, struct vtun_host *host)
|
||||||
{
|
{
|
||||||
char *ptr, *p;
|
char *ptr, *p;
|
||||||
int s;
|
int s;
|
||||||
|
|
||||||
|
if (strlen(str) >= 20) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
if ((ptr = strchr(str, '<'))) {
|
if ((ptr = strchr(str, '<'))) {
|
||||||
vtun_syslog(LOG_DEBUG, "Remote Server sends %s.", ptr);
|
vtun_syslog(LOG_DEBUG, "Remote Server sends %s.", ptr);
|
||||||
ptr++;
|
ptr++;
|
||||||
@ -207,15 +177,17 @@ static int cf2bf(char *str, struct vtun_host *host)
|
|||||||
host->flags |= VTUN_KEEP_ALIVE;
|
host->flags |= VTUN_KEEP_ALIVE;
|
||||||
break;
|
break;
|
||||||
case 'C':
|
case 'C':
|
||||||
if((s = strtol(ptr,&p,10)) == ERANGE || ptr == p)
|
if ((s = strtol(ptr, &p, 10)) == ERANGE || ptr == p) {
|
||||||
return 0;
|
return -1;
|
||||||
|
}
|
||||||
host->flags |= VTUN_ZLIB;
|
host->flags |= VTUN_ZLIB;
|
||||||
host->zlevel = s;
|
host->zlevel = s;
|
||||||
ptr = p;
|
ptr = p;
|
||||||
break;
|
break;
|
||||||
case 'L':
|
case 'L':
|
||||||
if((s = strtol(ptr,&p,10)) == ERANGE || ptr == p)
|
if ((s = strtol(ptr, &p, 10)) == ERANGE || ptr == p) {
|
||||||
return 0;
|
return -1;
|
||||||
|
}
|
||||||
host->flags |= VTUN_LZO;
|
host->flags |= VTUN_LZO;
|
||||||
host->zlevel = s;
|
host->zlevel = s;
|
||||||
ptr = p;
|
ptr = p;
|
||||||
@ -225,16 +197,17 @@ static int cf2bf(char *str, struct vtun_host *host)
|
|||||||
ptr==p check */
|
ptr==p check */
|
||||||
if ((s = strtol(ptr, &p, 10)) == ERANGE) {
|
if ((s = strtol(ptr, &p, 10)) == ERANGE) {
|
||||||
vtun_syslog(LOG_ERR, "Garbled encryption method. Bailing out.");
|
vtun_syslog(LOG_ERR, "Garbled encryption method. Bailing out.");
|
||||||
return 0;
|
return -1;
|
||||||
}
|
}
|
||||||
host->flags |= VTUN_ENCRYPT;
|
host->flags |= VTUN_ENCRYPT;
|
||||||
host->cipher = s;
|
host->cipher = s;
|
||||||
ptr = p;
|
ptr = p;
|
||||||
break;
|
break;
|
||||||
case 'S':
|
case 'S':
|
||||||
if((s = strtol(ptr,&p,10)) == ERANGE || ptr == p)
|
if ((s = strtol(ptr, &p, 10)) == ERANGE || ptr == p) {
|
||||||
return 0;
|
return -1;
|
||||||
if( s ){
|
}
|
||||||
|
if (s > 0) {
|
||||||
host->flags |= VTUN_SHAPE;
|
host->flags |= VTUN_SHAPE;
|
||||||
host->spd_out = s;
|
host->spd_out = s;
|
||||||
}
|
}
|
||||||
@ -244,163 +217,256 @@ static int cf2bf(char *str, struct vtun_host *host)
|
|||||||
/* reserved for Feature transmit */
|
/* reserved for Feature transmit */
|
||||||
break;
|
break;
|
||||||
case '>':
|
case '>':
|
||||||
return 1;
|
return 0;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return -1;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Functions to convert binary key data to character string.
|
|
||||||
* string format: <char_data>
|
|
||||||
*/
|
|
||||||
|
|
||||||
static char *cl2cs(char *chal)
|
|
||||||
{
|
|
||||||
static char str[VTUN_CHAL_SIZE*2+3], *chr="abcdefghijklmnop";
|
|
||||||
register char *ptr = str;
|
|
||||||
register int i;
|
|
||||||
|
|
||||||
*(ptr++) = '<';
|
|
||||||
for(i=0; i<VTUN_CHAL_SIZE; i++){
|
|
||||||
*(ptr++) = chr[ ((chal[i] & 0xf0) >> 4) ];
|
|
||||||
*(ptr++) = chr[ (chal[i] & 0x0f) ];
|
|
||||||
}
|
|
||||||
|
|
||||||
*(ptr++) = '>';
|
|
||||||
*ptr = '\0';
|
|
||||||
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int cs2cl(char *str, char *chal)
|
|
||||||
{
|
|
||||||
register char *ptr = str;
|
|
||||||
register int i;
|
|
||||||
|
|
||||||
if( !(ptr = strchr(str,'<')) )
|
|
||||||
return 0;
|
|
||||||
ptr++;
|
|
||||||
if( !strtok(ptr,">") || strlen(ptr) != VTUN_CHAL_SIZE*2 )
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for(i=0; i<VTUN_CHAL_SIZE && *ptr; i++, ptr+=2) {
|
|
||||||
chal[i] = (*ptr - 'a') << 4;
|
|
||||||
chal[i] |= *(ptr+1) - 'a';
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Authentication (Server side) */
|
/* Authentication (Server side) */
|
||||||
struct vtun_host *auth_server(int fd)
|
struct vtun_host *auth_server(int fd)
|
||||||
{
|
{
|
||||||
char chal_req[VTUN_CHAL_SIZE], chal_res[VTUN_CHAL_SIZE];
|
char buf[VTUN_MESG_SIZE], *str1, *str2, *str3;
|
||||||
char buf[VTUN_MESG_SIZE], *str1, *str2;
|
unsigned char cack[crypto_generichash_BYTES];
|
||||||
struct vtun_host *h = NULL;
|
unsigned char client_pk[crypto_scalarmult_BYTES];
|
||||||
char *host = NULL;
|
unsigned char server_pk[crypto_scalarmult_BYTES];
|
||||||
|
unsigned char server_sk[crypto_scalarmult_SCALARBYTES];
|
||||||
|
unsigned char ckey[4 + crypto_scalarmult_BYTES + crypto_generichash_BYTES];
|
||||||
|
unsigned char dhkey[crypto_scalarmult_BYTES];
|
||||||
|
unsigned char skey[crypto_scalarmult_BYTES + crypto_generichash_BYTES];
|
||||||
|
char skey_hex[2 * (crypto_scalarmult_BYTES + crypto_generichash_BYTES) + 1];
|
||||||
|
unsigned char hash[crypto_generichash_BYTES];
|
||||||
|
unsigned char flhash[crypto_generichash_BYTES];
|
||||||
|
char flhash_hex[2 * crypto_generichash_BYTES + 1];
|
||||||
|
struct vtun_host *host = NULL;
|
||||||
|
char *flags;
|
||||||
|
char *host_name = NULL;
|
||||||
|
crypto_generichash_state st;
|
||||||
|
time_t client_now;
|
||||||
|
size_t bin_len;
|
||||||
int stage;
|
int stage;
|
||||||
|
int success = 0;
|
||||||
|
|
||||||
set_title("authentication");
|
set_title("authentication");
|
||||||
|
|
||||||
print_p(fd, "VTUN server ver %s\n", VTUN_VER);
|
print_p(fd, "VTUN server ver %s\n", VTUN_VER);
|
||||||
|
|
||||||
stage = ST_HOST;
|
stage = ST_STEP2;
|
||||||
|
|
||||||
while (readn_t(fd, buf, VTUN_MESG_SIZE, vtun.timeout) > 0) {
|
while (readn_t(fd, buf, VTUN_MESG_SIZE, vtun.timeout) > 0) {
|
||||||
buf[sizeof(buf) - 1] = '\0';
|
buf[sizeof(buf) - 1] = '\0';
|
||||||
strtok(buf, "\r\n");
|
strtok(buf, "\r\n");
|
||||||
|
if (!(str1 = strtok(buf, " :"))) {
|
||||||
if( !(str1=strtok(buf," :")) )
|
|
||||||
break;
|
break;
|
||||||
if( !(str2=strtok(NULL," :")) )
|
}
|
||||||
|
if (!(str2 = strtok(NULL, " :"))) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
switch (stage) {
|
switch (stage) {
|
||||||
case ST_HOST:
|
case ST_STEP2:
|
||||||
if( !strcmp(str1,"HOST") ){
|
if (!strcmp(str1, "CKEY")) {
|
||||||
host = strdup(str2);
|
if (!(str3 = strtok(NULL, " \t"))) {
|
||||||
|
break;
|
||||||
gen_chal(chal_req);
|
}
|
||||||
print_p(fd,"OK CHAL: %s\n", cl2cs(chal_req));
|
sodium_hex2bin(ckey, sizeof ckey, str3, strlen(str3), "", &bin_len, NULL);
|
||||||
|
if (bin_len != sizeof ckey) {
|
||||||
stage = ST_CHAL;
|
break;
|
||||||
|
}
|
||||||
|
client_now = ((time_t) ckey[0]) << 24 | ((time_t) ckey[1]) << 16 |
|
||||||
|
((time_t) ckey[2]) << 9 | ((time_t) ckey[3]);
|
||||||
|
(void) client_now;
|
||||||
|
host_name = str2;
|
||||||
|
if ((host = find_host(host_name)) == NULL || derive_key(host) != 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
crypto_generichash(hash, sizeof hash,
|
||||||
|
ckey, 4 + crypto_scalarmult_BYTES,
|
||||||
|
host->akey, crypto_generichash_KEYBYTES);
|
||||||
|
if (sodium_memcmp(hash, ckey + 4 + crypto_scalarmult_BYTES, sizeof hash) != 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
memcpy(client_pk, ckey + 4, sizeof client_pk);
|
||||||
|
randombytes_buf(server_sk, crypto_scalarmult_SCALARBYTES);
|
||||||
|
crypto_scalarmult_base(server_pk, server_sk);
|
||||||
|
memcpy(skey, server_pk, sizeof server_pk);
|
||||||
|
memcpy(skey + crypto_scalarmult_BYTES, hash, sizeof hash);
|
||||||
|
crypto_generichash(skey + crypto_scalarmult_BYTES, crypto_scalarmult_BYTES,
|
||||||
|
skey, sizeof skey,
|
||||||
|
host->akey, crypto_generichash_KEYBYTES);
|
||||||
|
sodium_bin2hex(skey_hex, sizeof skey_hex, skey, sizeof skey);
|
||||||
|
print_p(fd, "SKEY: %s\n", skey_hex);
|
||||||
|
stage = ST_STEP3;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ST_CHAL:
|
|
||||||
if( !strcmp(str1,"CHAL") ){
|
case ST_STEP3:
|
||||||
if( !cs2cl(str2,chal_res) )
|
if (!strcmp(str1, "CACK")) {
|
||||||
|
sodium_hex2bin(cack, sizeof cack, str2, strlen(str2), "", &bin_len, NULL);
|
||||||
|
if (bin_len != sizeof cack) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
if( !(h = find_host(host)) )
|
crypto_generichash_init(&st, host->akey, crypto_generichash_KEYBYTES,
|
||||||
|
crypto_generichash_BYTES);
|
||||||
|
crypto_generichash_update(&st, (const unsigned char *) "CACK", 4);
|
||||||
|
crypto_generichash_update(&st, skey, sizeof skey);
|
||||||
|
crypto_generichash_final(&st, hash, sizeof hash);
|
||||||
|
if (sodium_memcmp(hash, cack, sizeof hash) != 0) {
|
||||||
break;
|
break;
|
||||||
derive_key(h);
|
}
|
||||||
auth_chal(chal_req, h);
|
|
||||||
|
|
||||||
if( !sodium_memcmp(chal_req, chal_res, VTUN_CHAL_SIZE) ){
|
|
||||||
/* Auth successeful. */
|
|
||||||
|
|
||||||
/* Lock host */
|
/* Lock host */
|
||||||
if( lock_host(h) < 0 ){
|
if (lock_host(host) < 0) {
|
||||||
/* Multiple connections are denied */
|
/* Multiple connections are denied */
|
||||||
h = NULL;
|
host = NULL;
|
||||||
break;
|
|
||||||
}
|
|
||||||
print_p(fd,"OK FLAGS: %s\n", bf2cf(h));
|
|
||||||
} else
|
|
||||||
h = NULL;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
flags = bf2cf(host);
|
||||||
|
crypto_generichash_init(&st, host->akey, crypto_generichash_KEYBYTES,
|
||||||
|
crypto_generichash_BYTES);
|
||||||
|
crypto_generichash_update(&st, (const unsigned char *) flags, strlen(flags));
|
||||||
|
crypto_generichash_update(&st, cack, sizeof cack);
|
||||||
|
crypto_generichash_final(&st, flhash, sizeof flhash);
|
||||||
|
sodium_bin2hex(flhash_hex, sizeof flhash_hex, flhash, sizeof flhash);
|
||||||
|
print_p(fd, "FLAGS: %s %s\n", flags, flhash_hex);
|
||||||
|
|
||||||
if( host )
|
crypto_scalarmult(dhkey, server_sk, client_pk);
|
||||||
free(host);
|
sodium_memzero(server_sk, sizeof server_sk);
|
||||||
|
if ((host->key = sodium_malloc(HOST_KEYBYTES)) == NULL) {
|
||||||
if( !h )
|
abort();
|
||||||
|
}
|
||||||
|
crypto_generichash(host->key, HOST_KEYBYTES, dhkey, sizeof dhkey,
|
||||||
|
host->akey, crypto_generichash_KEYBYTES);
|
||||||
|
sodium_memzero(dhkey, sizeof dhkey);
|
||||||
|
success = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (success == 0) {
|
||||||
print_p(fd, "ERR\n");
|
print_p(fd, "ERR\n");
|
||||||
|
host = NULL;
|
||||||
return h;
|
}
|
||||||
|
return host;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Authentication (Client side) */
|
/* Authentication (Client side) */
|
||||||
int auth_client(int fd, struct vtun_host *host)
|
int auth_client(int fd, struct vtun_host *host)
|
||||||
{
|
{
|
||||||
char buf[VTUN_MESG_SIZE], chal[VTUN_CHAL_SIZE];
|
char buf[VTUN_MESG_SIZE], *str1, *str2, *str3;
|
||||||
int stage, success=0 ;
|
unsigned char cack[crypto_generichash_BYTES];
|
||||||
|
char cack_hex[2 * crypto_generichash_BYTES + 1];
|
||||||
|
unsigned char flhash[crypto_generichash_BYTES];
|
||||||
|
unsigned char hash[crypto_generichash_BYTES];
|
||||||
|
unsigned char client_pk[crypto_scalarmult_BYTES];
|
||||||
|
unsigned char client_sk[crypto_scalarmult_SCALARBYTES];
|
||||||
|
unsigned char server_pk[crypto_scalarmult_BYTES];
|
||||||
|
unsigned char dhkey[crypto_scalarmult_BYTES];
|
||||||
|
unsigned char ckey[4 + crypto_scalarmult_BYTES + crypto_generichash_BYTES];
|
||||||
|
char ckey_hex[2 * (4 + crypto_scalarmult_BYTES + crypto_generichash_BYTES) + 1];
|
||||||
|
unsigned char skey[crypto_scalarmult_BYTES + crypto_generichash_BYTES];
|
||||||
|
crypto_generichash_state st;
|
||||||
|
time_t now;
|
||||||
|
size_t bin_len;
|
||||||
|
int stage;
|
||||||
|
int success = 0;
|
||||||
|
|
||||||
stage = ST_INIT;
|
stage = ST_INIT;
|
||||||
|
|
||||||
|
if (derive_key(host) != 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
while (readn_t(fd, buf, VTUN_MESG_SIZE, vtun.timeout) > 0) {
|
while (readn_t(fd, buf, VTUN_MESG_SIZE, vtun.timeout) > 0) {
|
||||||
buf[sizeof(buf) - 1] = '\0';
|
buf[sizeof(buf) - 1] = '\0';
|
||||||
|
strtok(buf, "\r\n");
|
||||||
|
if (!(str1 = strtok(buf, " :"))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!(str2 = strtok(NULL, " :"))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
switch (stage) {
|
switch (stage) {
|
||||||
case ST_INIT:
|
case ST_INIT:
|
||||||
if( !strncmp(buf,"VTUN",4) ){
|
if (!strcmp(str1, "VTUN")) {
|
||||||
stage = ST_HOST;
|
now = time(NULL);
|
||||||
print_p(fd,"HOST: %s\n",host->host);
|
randombytes_buf(client_sk, crypto_scalarmult_SCALARBYTES);
|
||||||
|
crypto_scalarmult_base(client_pk, client_sk);
|
||||||
|
ckey[0] = (unsigned char)(now >> 24);
|
||||||
|
ckey[1] = (unsigned char)(now >> 16);
|
||||||
|
ckey[2] = (unsigned char)(now >> 8);
|
||||||
|
ckey[3] = (unsigned char)(now);
|
||||||
|
memcpy(ckey + 4, client_pk, crypto_scalarmult_BYTES);
|
||||||
|
crypto_generichash(ckey + 4 + crypto_scalarmult_BYTES, crypto_generichash_BYTES,
|
||||||
|
ckey, 4 + crypto_scalarmult_BYTES,
|
||||||
|
host->akey, crypto_generichash_KEYBYTES);
|
||||||
|
sodium_bin2hex(ckey_hex, sizeof ckey_hex, ckey, sizeof ckey);
|
||||||
|
stage = ST_STEP2;
|
||||||
|
print_p(fd, "CKEY: %s %s\n", host->host, ckey_hex);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ST_HOST:
|
case ST_STEP2:
|
||||||
if( !strncmp(buf,"OK",2) && cs2cl(buf,chal)){
|
if (!strcmp(str1, "SKEY")) {
|
||||||
stage = ST_CHAL;
|
sodium_hex2bin(skey, sizeof skey, str2, strlen(str2), "", &bin_len, NULL);
|
||||||
derive_key(host);
|
if (bin_len != sizeof skey) {
|
||||||
auth_chal(chal, host);
|
break;
|
||||||
print_p(fd,"CHAL: %s\n", cl2cs(chal));
|
}
|
||||||
|
crypto_generichash_init(&st, host->akey, crypto_generichash_KEYBYTES,
|
||||||
|
crypto_generichash_BYTES);
|
||||||
|
crypto_generichash_update(&st, skey, crypto_scalarmult_BYTES);
|
||||||
|
crypto_generichash_update(&st, ckey + 4 + crypto_scalarmult_BYTES,
|
||||||
|
crypto_generichash_BYTES);
|
||||||
|
crypto_generichash_final(&st, hash, sizeof hash);
|
||||||
|
if (sodium_memcmp(hash, skey + crypto_scalarmult_BYTES, sizeof hash) != 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
memcpy(server_pk, skey, sizeof server_pk);
|
||||||
|
crypto_generichash_init(&st, host->akey, crypto_generichash_KEYBYTES,
|
||||||
|
crypto_generichash_BYTES);
|
||||||
|
crypto_generichash_update(&st, (const unsigned char *) "CACK", 4);
|
||||||
|
crypto_generichash_update(&st, skey, sizeof skey);
|
||||||
|
crypto_generichash_final(&st, cack, sizeof cack);
|
||||||
|
sodium_bin2hex(cack_hex, sizeof cack_hex, cack, sizeof cack);
|
||||||
|
print_p(fd, "CACK: %s\n", cack_hex);
|
||||||
|
stage = ST_STEP3;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ST_CHAL:
|
case ST_STEP3:
|
||||||
if( !strncmp(buf,"OK",2) && cf2bf(buf,host) )
|
if (!strcmp(str1, "FLAGS")) {
|
||||||
|
if (!(str3 = strtok(NULL, " \t"))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sodium_hex2bin(flhash, sizeof flhash, str3, strlen(str3), "", &bin_len, NULL);
|
||||||
|
if (bin_len != sizeof flhash) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (cf2bf(str2, host) != 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
crypto_generichash_init(&st, host->akey, crypto_generichash_KEYBYTES,
|
||||||
|
crypto_generichash_BYTES);
|
||||||
|
crypto_generichash_update(&st, (const unsigned char *) str2, strlen(str2));
|
||||||
|
crypto_generichash_update(&st, cack, sizeof cack);
|
||||||
|
crypto_generichash_final(&st, hash, sizeof hash);
|
||||||
|
if (sodium_memcmp(hash, flhash, sizeof hash) != 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
crypto_scalarmult(dhkey, client_sk, server_pk);
|
||||||
|
sodium_memzero(client_sk, sizeof client_sk);
|
||||||
|
if ((host->key = sodium_malloc(HOST_KEYBYTES)) == NULL) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
crypto_generichash(host->key, HOST_KEYBYTES, dhkey, sizeof dhkey,
|
||||||
|
host->akey, crypto_generichash_KEYBYTES);
|
||||||
|
sodium_memzero(dhkey, sizeof dhkey);
|
||||||
success = 1;
|
success = 1;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
6
auth.h
6
auth.h
@ -20,11 +20,9 @@
|
|||||||
* $Id: auth.h,v 1.3.2.2 2008/01/07 22:35:19 mtbishop Exp $
|
* $Id: auth.h,v 1.3.2.2 2008/01/07 22:35:19 mtbishop Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define VTUN_CHAL_SIZE 16
|
|
||||||
|
|
||||||
#define ST_INIT 0
|
#define ST_INIT 0
|
||||||
#define ST_HOST 1
|
#define ST_STEP2 1
|
||||||
#define ST_CHAL 2
|
#define ST_STEP3 2
|
||||||
|
|
||||||
struct vtun_host * auth_server(int fd);
|
struct vtun_host * auth_server(int fd);
|
||||||
int auth_client(int fd, struct vtun_host *host);
|
int auth_client(int fd, struct vtun_host *host);
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
|
#include <sodium.h>
|
||||||
|
|
||||||
#include "compat.h"
|
#include "compat.h"
|
||||||
#include "vtun.h"
|
#include "vtun.h"
|
||||||
@ -107,6 +108,7 @@ statement: '\n'
|
|||||||
memcpy(parse_host, &default_host, sizeof(struct vtun_host));
|
memcpy(parse_host, &default_host, sizeof(struct vtun_host));
|
||||||
parse_host->host = strdup($1);
|
parse_host->host = strdup($1);
|
||||||
parse_host->passwd = NULL;
|
parse_host->passwd = NULL;
|
||||||
|
parse_host->akey = NULL;
|
||||||
parse_host->key = NULL;
|
parse_host->key = NULL;
|
||||||
parse_host->sopt.host = strdup($1);
|
parse_host->sopt.host = strdup($1);
|
||||||
|
|
||||||
@ -575,6 +577,8 @@ int free_host(void *d, void *u)
|
|||||||
|
|
||||||
free(h->host);
|
free(h->host);
|
||||||
free(h->passwd);
|
free(h->passwd);
|
||||||
|
sodium_free(h->akey);
|
||||||
|
sodium_free(h->key);
|
||||||
|
|
||||||
llist_free(&h->up, free_cmd, NULL);
|
llist_free(&h->up, free_cmd, NULL);
|
||||||
llist_free(&h->down, free_cmd, NULL);
|
llist_free(&h->down, free_cmd, NULL);
|
||||||
|
@ -35,16 +35,6 @@ typedef struct CryptoCtx {
|
|||||||
|
|
||||||
static CryptoCtx ctx;
|
static CryptoCtx ctx;
|
||||||
|
|
||||||
static int
|
|
||||||
derive_key(unsigned char *key, size_t key_size, struct vtun_host *host)
|
|
||||||
{
|
|
||||||
crypto_generichash(key, key_size, host->key, HOST_KEYBYTES, NULL, 0U);
|
|
||||||
sodium_free(host->key);
|
|
||||||
host->key = NULL;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
init_nonce(unsigned char *nonce, size_t nonce_size)
|
init_nonce(unsigned char *nonce, size_t nonce_size)
|
||||||
{
|
{
|
||||||
@ -69,18 +59,12 @@ init_nonce(unsigned char *nonce, size_t nonce_size)
|
|||||||
static int
|
static int
|
||||||
alloc_encrypt(struct vtun_host *host)
|
alloc_encrypt(struct vtun_host *host)
|
||||||
{
|
{
|
||||||
unsigned char *key;
|
|
||||||
|
|
||||||
if (sodium_init() < 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
key = sodium_malloc(crypto_aead_KEYBYTES);
|
|
||||||
ctx.state = sodium_malloc(sizeof *ctx.state);
|
ctx.state = sodium_malloc(sizeof *ctx.state);
|
||||||
ctx.message = sodium_malloc(MESSAGE_MAX_SIZE);
|
ctx.message = sodium_malloc(MESSAGE_MAX_SIZE);
|
||||||
ctx.ciphertext = sodium_malloc(CIPHERTEXT_MAX_TOTAL_SIZE);
|
ctx.ciphertext = sodium_malloc(CIPHERTEXT_MAX_TOTAL_SIZE);
|
||||||
ctx.nonce = sodium_malloc(crypto_aead_NPUBBYTES);
|
ctx.nonce = sodium_malloc(crypto_aead_NPUBBYTES);
|
||||||
ctx.previous_decrypted_nonce = sodium_malloc(crypto_aead_NPUBBYTES);
|
ctx.previous_decrypted_nonce = sodium_malloc(crypto_aead_NPUBBYTES);
|
||||||
if (key == NULL || ctx.state == NULL || ctx.message == NULL ||
|
if (host->key == NULL || ctx.state == NULL || ctx.message == NULL ||
|
||||||
ctx.ciphertext == NULL || ctx.ciphertext == NULL || ctx.nonce == NULL ||
|
ctx.ciphertext == NULL || ctx.ciphertext == NULL || ctx.nonce == NULL ||
|
||||||
ctx.previous_decrypted_nonce == NULL) {
|
ctx.previous_decrypted_nonce == NULL) {
|
||||||
abort();
|
abort();
|
||||||
@ -88,11 +72,9 @@ alloc_encrypt(struct vtun_host *host)
|
|||||||
if (init_nonce(ctx.nonce, crypto_aead_NPUBBYTES) != 0) {
|
if (init_nonce(ctx.nonce, crypto_aead_NPUBBYTES) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (derive_key(key, crypto_aead_KEYBYTES, host) != 0) {
|
crypto_aead_aes256gcm_beforenm(ctx.state, host->key);
|
||||||
return -1;
|
sodium_free(host->key);
|
||||||
}
|
host->key = NULL;
|
||||||
crypto_aead_aes256gcm_beforenm(ctx.state, key);
|
|
||||||
sodium_free(key);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -134,7 +116,8 @@ encrypt_buf(int message_len_, char *message_, char ** const ciphertext_p)
|
|||||||
crypto_aead_aes256gcm_encrypt_afternm(ctx.ciphertext, &ciphertext_len,
|
crypto_aead_aes256gcm_encrypt_afternm(ctx.ciphertext, &ciphertext_len,
|
||||||
message, message_len,
|
message, message_len,
|
||||||
NULL, 0ULL,
|
NULL, 0ULL,
|
||||||
NULL, ctx.nonce, ctx.state);
|
NULL, ctx.nonce,
|
||||||
|
(const crypto_aead_aes256gcm_state *) ctx.state);
|
||||||
memcpy(ctx.ciphertext + message_len + crypto_aead_ABYTES,
|
memcpy(ctx.ciphertext + message_len + crypto_aead_ABYTES,
|
||||||
ctx.nonce, crypto_aead_NPUBBYTES);
|
ctx.nonce, crypto_aead_NPUBBYTES);
|
||||||
sodium_increment(ctx.nonce, crypto_aead_NPUBBYTES);
|
sodium_increment(ctx.nonce, crypto_aead_NPUBBYTES);
|
||||||
@ -160,7 +143,8 @@ decrypt_buf(int ciphertext_len_, char *ciphertext_, char ** const message_p)
|
|||||||
if (is_lower_or_equal(nonce, ctx.previous_decrypted_nonce, crypto_aead_NPUBBYTES) ||
|
if (is_lower_or_equal(nonce, ctx.previous_decrypted_nonce, crypto_aead_NPUBBYTES) ||
|
||||||
crypto_aead_aes256gcm_decrypt_afternm(ctx.message, &message_len, NULL,
|
crypto_aead_aes256gcm_decrypt_afternm(ctx.message, &message_len, NULL,
|
||||||
ciphertext, ciphertext_len,
|
ciphertext, ciphertext_len,
|
||||||
NULL, 0ULL, nonce, ctx.state) != 0) {
|
NULL, 0ULL, nonce,
|
||||||
|
(const crypto_aead_aes256gcm_state *) ctx.state) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
memcpy(ctx.previous_decrypted_nonce, nonce, crypto_aead_NPUBBYTES);
|
memcpy(ctx.previous_decrypted_nonce, nonce, crypto_aead_NPUBBYTES);
|
||||||
|
4
lib.c
4
lib.c
@ -122,7 +122,9 @@ int print_p(int fd,const char *fmt, ...)
|
|||||||
|
|
||||||
/* print the argument string */
|
/* print the argument string */
|
||||||
va_start(ap, fmt);
|
va_start(ap, fmt);
|
||||||
vsnprintf(buf,sizeof(buf)-1, fmt, ap);
|
if (vsnprintf(buf,sizeof(buf)-1, fmt, ap) >= VTUN_MESG_SIZE)
|
||||||
|
abort();
|
||||||
|
|
||||||
va_end(ap);
|
va_end(ap);
|
||||||
|
|
||||||
return write_n(fd, buf, sizeof(buf));
|
return write_n(fd, buf, sizeof(buf));
|
||||||
|
3
server.c
3
server.c
@ -43,6 +43,8 @@
|
|||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <sodium.h>
|
||||||
|
|
||||||
#include "vtun.h"
|
#include "vtun.h"
|
||||||
#include "lib.h"
|
#include "lib.h"
|
||||||
#include "lock.h"
|
#include "lock.h"
|
||||||
@ -66,6 +68,7 @@ static void connection(int sock)
|
|||||||
char *ip;
|
char *ip;
|
||||||
int opt;
|
int opt;
|
||||||
|
|
||||||
|
randombytes_stir();
|
||||||
opt = sizeof(struct sockaddr_in);
|
opt = sizeof(struct sockaddr_in);
|
||||||
if( getpeername(sock, (struct sockaddr *) &cl_addr, &opt) ){
|
if( getpeername(sock, (struct sockaddr *) &cl_addr, &opt) ){
|
||||||
vtun_syslog(LOG_ERR, "Can't get peer name");
|
vtun_syslog(LOG_ERR, "Can't get peer name");
|
||||||
|
3
vtun.h
3
vtun.h
@ -87,6 +87,7 @@ struct vtun_addr {
|
|||||||
struct vtun_host {
|
struct vtun_host {
|
||||||
char *host;
|
char *host;
|
||||||
char *passwd;
|
char *passwd;
|
||||||
|
unsigned char *akey;
|
||||||
unsigned char *key;
|
unsigned char *key;
|
||||||
char *dev;
|
char *dev;
|
||||||
|
|
||||||
@ -171,7 +172,7 @@ extern llist host_list;
|
|||||||
#define VTUN_BAD_FRAME 0x8000
|
#define VTUN_BAD_FRAME 0x8000
|
||||||
|
|
||||||
/* Authentication message size */
|
/* Authentication message size */
|
||||||
#define VTUN_MESG_SIZE 50
|
#define VTUN_MESG_SIZE 256
|
||||||
|
|
||||||
/* Support for multiple connections */
|
/* Support for multiple connections */
|
||||||
#define VTUN_MULTI_DENY 0 /* no */
|
#define VTUN_MULTI_DENY 0 /* no */
|
||||||
|
Loading…
Reference in New Issue
Block a user