commit dc9b14d9875bef6247ce8e125ca1bac67c206029
parent aa2987b54fb2142ed6ae11eed897296b102b80bd
Author: sin <sin@2f30.org>
Date: Wed, 6 Apr 2016 14:04:59 +0100
use evp aead api
Diffstat:
M | stun.8 | | | 9 | ++++++++- |
M | stun.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);