stun

simple point to point tunnel
git clone git://git.2f30.org/stun
Log | Files | Refs | README

commit dc9b14d9875bef6247ce8e125ca1bac67c206029
parent aa2987b54fb2142ed6ae11eed897296b102b80bd
Author: sin <sin@2f30.org>
Date:   Wed,  6 Apr 2016 14:04:59 +0100

use evp aead api

Diffstat:
Mstun.8 | 9++++++++-
Mstun.c | 285++++++++++++++++++++++++++++++++++++++-----------------------------------------
2 files changed, 145 insertions(+), 149 deletions(-)

diff --git a/stun.8 b/stun.8 @@ -1,4 +1,4 @@ -.Dd Mar 30, 2016 +.Dd April 6, 2016 .Dt STUN 8 .Os .Sh NAME @@ -11,12 +11,14 @@ .Op Fl b Ar address .Op Fl p Ar port .Op Fl t Ar devtype +.Op Fl c Ar cipher .Ar interface .Nm stun .Op Fl df .Fl h Ar host .Op Fl p Ar port .Op Fl t Ar devtype +.Op Fl c Ar cipher .Ar interface .Sh DESCRIPTION .Nm @@ -45,6 +47,11 @@ Connect to specified Select the tunnel .Ar device type . The two available device types are TUN and TAP. The default is TUN. +.It Fl c Ar cipher +Use the given +.Ar cipher . +One can choose between aes-128-gcm, aes-256-gcm, chacha20-poly1305 +and chacha20-poly1305-ietf. The default cipher is chacha20-poly1305. .El .Sh BUGS This program is an experiment and may not be secure. Use at your diff --git a/stun.c b/stun.c @@ -5,10 +5,6 @@ * layer 2 (TAP) and layer 3 (TUN) tunnels. TCP is the only supported * transport. * - * All communication is encrypted using a pre-shared symmetric key. - * aes-256-gcm was chosen because it is fast and does authentication. - * The key is derived with PBKDF2. - * * stun is a client-server design. There can only be a single client * connected to the server at any time. Routing between clients is handled * by the networking stack on the server side. @@ -31,11 +27,8 @@ * * The stun packet format is shown below: * - * [TAG] [IV] [PAYLOAD LENGTH] [PAYLOAD] + * [NONCE] [PAYLOAD LEN] [PAYLOAD] [TAG] * - * The tag is used for authentication and it is 16 octets. - * The iv is randomly generated for each packet and it is 12 octets. - * The payload length is 2 octets. */ #include <sys/types.h> @@ -72,7 +65,6 @@ #include <time.h> #include <unistd.h> -#include <openssl/aes.h> #include <openssl/evp.h> #include "arg.h" @@ -82,23 +74,22 @@ #define explicit_bzero bzero #endif -#define NOPRIVUSER "nobody" -#define RCVTIMEO 250 /* in milliseconds */ -#define RECONNECTTIMEO 60 /* in seconds */ -#define TAGLEN 16 -#define IVLEN 12 -#define HDRLEN 2 -#define MAXPAYLOADLEN 1428 -#define MAXPKTLEN (TAGLEN + IVLEN + HDRLEN + MAXPAYLOADLEN) -#define BADPKT 0x1000 +#define NOPRIVUSER "nobody" +#define RCVTIMEO 250 /* in milliseconds */ +#define RECONNECTTIMEO 60 /* in seconds */ +#define HDRLEN 2 +#define MAXPAYLOADLEN 1424 +#define BADPKT 0x1000 +#define NROUNDS 100000 +#define DEFCIPHER "chacha20-poly1305" enum { TUNDEV, TAPDEV }; -EVP_CIPHER_CTX ectx, dctx; -unsigned char aeskey[EVP_MAX_KEY_LENGTH]; +EVP_AEAD_CTX ectx, dctx; +const EVP_AEAD *aead; char *argv0; char *bindaddr; char *host; @@ -245,84 +236,6 @@ revokeprivs(void) logerr("failed to revoke privs"); } -int -derivekey(char *pw, int pwlen) -{ - int nrounds = 100000; - - if (PKCS5_PBKDF2_HMAC_SHA1(pw, pwlen, NULL, 0, nrounds, 32, aeskey) != 1) - logerr("PKCS5_PBKDF2_HMAC_SHA1 failed"); - return 0; -} - -int -aesinit(EVP_CIPHER_CTX *ectx, EVP_CIPHER_CTX *dctx) -{ - EVP_CIPHER_CTX_init(ectx); - EVP_CIPHER_CTX_init(dctx); - return 0; -} - -int -aesenc(EVP_CIPHER_CTX *ctx, unsigned char *ct, unsigned char *pt, int ptlen, - unsigned char *key, unsigned char *iv, unsigned char *aad, int aadlen, - unsigned char *tag, int taglen) -{ - int len, flen; - - /* initialize encryption operation with the given key and iv */ - if (EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, key, iv) != 1) - logerr("EVP_EncryptInit_ex failed"); - - /* encrypt additional authentication data */ - if (EVP_EncryptUpdate(ctx, NULL, &len, aad, aadlen) != 1) - logerr("EVP_EncryptUpdate failed"); - - /* encrypt payload */ - if (EVP_EncryptUpdate(ctx, ct, &len, pt, ptlen) != 1) - logerr("EVP_EncryptUpdate failed"); - - /* finalize encryption */ - if (EVP_EncryptFinal_ex(ctx, ct + len, &flen) != 1) - logerr("EVP_EncryptFinal_ex failed"); - - /* get the tag */ - if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, TAGLEN, tag) != 1) - logerr("EVP_CTRL_GCM_GET_TAG failed"); - - return len + flen; -} - -int -aesdec(EVP_CIPHER_CTX *ctx, unsigned char *pt, unsigned char *ct, int ctlen, - unsigned char *key, unsigned char *iv, unsigned char *aad, int aadlen, - unsigned char *tag, int taglen) -{ - int len, flen; - - /* initialize decryption operation with the given key and iv */ - if (EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, key, iv) != 1) - logerr("EVP_DecryptInit_ex failed"); - - /* decrypt additional authentication data */ - if (EVP_DecryptUpdate(ctx, NULL, &len, aad, aadlen) != 1) - logerr("EVP_DecryptUpdate failed"); - - /* decrypt payload */ - if (EVP_DecryptUpdate(ctx, pt, &len, ct, ctlen) != 1) - logerr("EVP_DecryptUpdate failed"); - - /* set the tag */ - if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, TAGLEN, tag) != 1) - logerr("EVP_CTRL_GCM_SET_TAG failed"); - - /* finalize decryption and check if the tag matches */ - if (EVP_DecryptFinal_ex(ctx, pt + len, &flen) != 1) - return -1; - - return len + flen; -} - #if defined(__linux__) int opendev(char *dev) @@ -443,67 +356,91 @@ readdev(int fd, unsigned char *buf, int len) #endif int -writenet(int fd, unsigned char *pt, int len) +writenet(int fd, unsigned char *pt, int ptlen) { - unsigned char ct[MAXPAYLOADLEN]; - unsigned char hdr[HDRLEN], iv[IVLEN], tag[TAGLEN]; - unsigned char pkt[MAXPKTLEN]; - int pktlen; - - pack16(hdr, len); - arc4random_buf(iv, IVLEN); - aesenc(&ectx, ct, pt, len, aeskey, iv, hdr, HDRLEN, tag, TAGLEN); - memcpy(pkt, tag, TAGLEN); - memcpy(&pkt[TAGLEN], iv, IVLEN); - memcpy(&pkt[TAGLEN + IVLEN], hdr, HDRLEN); - memcpy(&pkt[TAGLEN + IVLEN + HDRLEN], ct, len); - pktlen = TAGLEN + IVLEN + HDRLEN + len; - return writeall(fd, pkt, pktlen); + unsigned char *pkt; + size_t noncelen = EVP_AEAD_nonce_length(aead); + size_t taglen = EVP_AEAD_max_tag_len(aead); + size_t pktlen = noncelen + HDRLEN + ptlen + taglen; + size_t outlen; + int n; + + if (!(pkt = malloc(pktlen))) + return -1; + arc4random_buf(pkt, noncelen); + pack16(&pkt[noncelen], ptlen); + if (!EVP_AEAD_CTX_seal(&ectx, &pkt[noncelen + HDRLEN], &outlen, + ptlen + taglen, pkt, noncelen, + pt, ptlen, &pkt[noncelen], HDRLEN)) + logerr("EVP_AEAD_CTX_seal failed"); + n = writeall(fd, pkt, pktlen); + free(pkt); + return n; } int -readnet(int fd, unsigned char *pt, int len) +readnet(int fd, unsigned char *pt, int ptlen) { - unsigned char ct[MAXPAYLOADLEN]; - unsigned char hdr[HDRLEN], iv[IVLEN], tag[TAGLEN]; + unsigned char *pkt; + size_t noncelen = EVP_AEAD_nonce_length(aead); + size_t taglen = EVP_AEAD_max_tag_len(aead); + size_t pktlen = noncelen + HDRLEN + ptlen + taglen; + size_t outlen; int n, ctlen; -#define CHECKERR(n) do { \ - if ((n) == 0) { \ - return 0; \ - } else if ((n) < 0) { \ - if (errno != EWOULDBLOCK) \ - return -1; \ - return BADPKT; \ - } \ -} while (0) - - n = readall(fd, tag, TAGLEN); - CHECKERR(n); + if (!(pkt = malloc(pktlen))) + return -1; - n = readall(fd, iv, IVLEN); - CHECKERR(n); + n = readall(fd, pkt, noncelen); + if (n == 0) { + free(pkt); + return 0; + } else if (n < 0) { + free(pkt); + if (errno != EWOULDBLOCK) + return -1; + return BADPKT; + } - n = readall(fd, hdr, HDRLEN); - CHECKERR(n); + n = readall(fd, &pkt[noncelen], HDRLEN); + if (n == 0) { + free(pkt); + return 0; + } else if (n < 0) { + free(pkt); + if (errno != EWOULDBLOCK) + return -1; + return BADPKT; + } - if ((ctlen = unpack16(hdr)) > MAXPAYLOADLEN) - ctlen = MAXPAYLOADLEN; + if ((ctlen = unpack16(&pkt[noncelen])) > ptlen) + ctlen = ptlen; - n = readall(fd, ct, ctlen); - CHECKERR(n); + n = readall(fd, &pkt[noncelen + HDRLEN], ctlen + taglen); + if (n == 0) { + free(pkt); + return 0; + } else if (n < 0) { + free(pkt); + if (errno != EWOULDBLOCK) + return -1; + return BADPKT; + } - n = aesdec(&dctx, pt, ct, ctlen, aeskey, iv, - hdr, HDRLEN, tag, TAGLEN); - if (n < 0) + if (!EVP_AEAD_CTX_open(&dctx, pt, &outlen, ptlen, pkt, noncelen, + &pkt[noncelen + HDRLEN], ctlen + taglen, + &pkt[noncelen], HDRLEN)) { + free(pkt); return BADPKT; - return n; + } + free(pkt); + return outlen; } int challenge(int netfd) { - unsigned char buf[MAXPAYLOADLEN]; + unsigned char buf[sizeof(uint64_t)]; struct pollfd pfd[1]; uint64_t n, reply; int ret; @@ -541,7 +478,7 @@ challenge(int netfd) int response(int netfd) { - unsigned char buf[MAXPAYLOADLEN]; + unsigned char buf[sizeof(uint64_t)]; uint64_t reply; int ret; @@ -711,17 +648,53 @@ err: } void +aead_from_name(const EVP_AEAD **aead, const char *name) +{ + *aead = NULL; + + if (!strcmp(name, "aes-128-gcm")) { +#ifndef OPENSSL_NO_AES + *aead = EVP_aead_aes_128_gcm(); +#else + logerr("No AES support"); +#endif + } else if (!strcmp(name, "aes-256-gcm")) { +#ifndef OPENSSL_NO_AES + *aead = EVP_aead_aes_256_gcm(); +#else + logerr("No AES support"); +#endif + } else if (!strcmp(name, "chacha20-poly1305")) { +#if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305) + *aead = EVP_aead_chacha20_poly1305(); +#else + logerr("No chacha20-poly1305 support"); +#endif + } else if (!strcmp(name, "chacha20-poly1305-ietf")) { +#if !defined(OPENSSL_NO_CHACHA) && !defined(OPENSSL_NO_POLY1305) + *aead = EVP_aead_chacha20_poly1305_ietf(); +#else + logerr("No chacha20-poly1305-ietf support"); +#endif + } else { + logerr("Unknown AEAD: %s", name); + } +} + +void usage(void) { - fprintf(stderr, "usage: stun [-df] -s [-b address] [-p port] [-t devtype] interface\n"); - fprintf(stderr, " stun [-df] -h host [-p port] [-t devtype] interface\n"); + fprintf(stderr, "usage: stun [-df] -s [-b address] [-p port] [-t devtype] [-c cipher] interface\n"); + fprintf(stderr, " stun [-df] -h host [-p port] [-t devtype] [-c cipher] interface\n"); exit(1); } int main(int argc, char *argv[]) { - char *arg, *pw; + unsigned char *key; + size_t keylen; + char *arg, *pw, *cipher = DEFCIPHER; int devfd; ARGBEGIN { @@ -752,6 +725,9 @@ main(int argc, char *argv[]) else usage(); break; + case 'c': + cipher = EARGF(usage()); + break; default: usage(); } ARGEND @@ -764,14 +740,27 @@ main(int argc, char *argv[]) daemon(0, 0); openlog("stun", LOG_PID | LOG_NDELAY, LOG_DAEMON); } - devfd = opendev(argv[0]); if (!(pw = getenv("STUNPW"))) logerr("STUNPW is not set"); - derivekey(pw, strlen(pw)); + + /* derive key from password */ + aead_from_name(&aead, cipher); + keylen = EVP_AEAD_key_length(aead); + if (!(key = malloc(keylen))) + logerr("out of memory"); + if (!PKCS5_PBKDF2_HMAC_SHA1(pw, strlen(pw), NULL, 0, NROUNDS, keylen, key)) + logerr("PKCS5_PBKDF2_HMAC_SHA1 failed"); explicit_bzero(pw, strlen(pw)); - aesinit(&ectx, &dctx); + + /* initialize cipher */ + if (!EVP_AEAD_CTX_init(&ectx, aead, key, keylen, + EVP_AEAD_DEFAULT_TAG_LENGTH, NULL)) + logerr("EVP_AEAD_CTX_init failed"); + if (!EVP_AEAD_CTX_init(&dctx, aead, key, keylen, + EVP_AEAD_DEFAULT_TAG_LENGTH, NULL)) + logerr("EVP_AEAD_CTX_init failed"); if (sflag) return serversetup(devfd);