commit fdcdd2572d4476996b65742a8cce502a321bea0c
parent b59586b4ba0af5a83f144466a9fa1d72ff2d2006
Author: sin <sin@2f30.org>
Date:   Tue, 29 Mar 2016 23:57:35 +0100
more crypto work
Diffstat:
| M | stun.c |  |  | 119 | ++++++++++++++++++++++++++++++++----------------------------------------------- | 
1 file changed, 48 insertions(+), 71 deletions(-)
diff --git a/stun.c b/stun.c
@@ -22,22 +22,14 @@
  * The sender verifies that the correct response was received.
  *
  * All communication is encrypted using a pre-shared symmetric key, currently
- * using aes-256-cbc hashed with sha1.
+ * using aes-256-cbc hashed with sha512.
  *
  * All tunneled traffic is encapsulated inside the TCP payload.
  * The packet format is shown below:
  *
- * [UNPADDED PAYLOAD LENGTH][PADDED PAYLOAD]
+ * [PAYLOAD LENGTH] [IV] [PAYLOAD]
  *
- * The payload is encrypted and padded to 16 bytes.  The payload length is a
- * two-octet field that describes the length of the unpadded payload.
- * When the receiver processes a packet it needs to pad the payload length in order
- * to decrypt the packet.  The unpadded length is then used to determine the size
- * of the actual decrypted payload.
- *
- * The maximum payload length allowed is 4095 bytes.  The maximum padded payload
- * length is therefore 4096 bytes.  Values at 0x8000 or above are reserved for internal
- * error conditions.
+ * Where payload length is 2 octets and IV is 16 octets.
  */
 
 #include <sys/types.h>
@@ -87,17 +79,18 @@
 #define CHALLENGETIMEO	1  /* in seconds */
 #define RECONNECTTIMEO	60 /* in seconds */
 #define HDRLEN		2
+#define IVLEN		16
 #define PKTLENMASK	0xfff
 #define BADPKT		0x8000
-#define MTU		1440
+#define MTU		1424
 
 enum {
 	TUNDEV,
 	TAPDEV
 };
 
-EVP_CIPHER_CTX enc, dec;
-unsigned char aeskey[32], aesiv[32];
+EVP_CIPHER_CTX ectx, dctx;
+unsigned char aeskey[EVP_MAX_KEY_LENGTH];
 char *argv0;
 char *host;
 char *port = "12080";
@@ -170,24 +163,12 @@ revokeprivs(void)
 }
 
 int
-padto(int len, int blocksize)
-{
-	return len + blocksize - len % blocksize;
-}
-
-int
-padlen(int len, int blocksize)
-{
-	return padto(len, blocksize) - len;
-}
-
-int
 prepkey(unsigned char *pw, int pwlen)
 {
 	int ret, nrounds = 5;
 
-	ret = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha1(),
-	                     NULL, pw, pwlen, nrounds, aeskey, aesiv);
+	ret = EVP_BytesToKey(EVP_aes_256_cbc(), EVP_sha512(),
+	                     NULL, pw, pwlen, nrounds, aeskey, NULL);
 	if (ret != 32)
 		logerr("wrong key size %d", ret);
 	return 0;
@@ -197,42 +178,38 @@ int
 aesinit(EVP_CIPHER_CTX *ectx, EVP_CIPHER_CTX *dctx)
 {
 	EVP_CIPHER_CTX_init(ectx);
-	EVP_EncryptInit_ex(ectx, EVP_aes_256_cbc(), NULL, NULL, NULL);
-	EVP_EncryptInit_ex(ectx, EVP_aes_256_cbc(), NULL, aeskey, aesiv);
-	EVP_CIPHER_CTX_set_padding(ectx, 0);
-
 	EVP_CIPHER_CTX_init(dctx);
-	EVP_DecryptInit_ex(dctx, EVP_aes_256_cbc(), NULL, NULL, NULL);
-	EVP_DecryptInit_ex(dctx, EVP_aes_256_cbc(), NULL, aeskey, aesiv);
-	EVP_CIPHER_CTX_set_padding(dctx, 0);
 	return 0;
 }
 
-void
-aesterm(EVP_CIPHER_CTX *ectx, EVP_CIPHER_CTX *dctx)
-{
-	EVP_CIPHER_CTX_cleanup(ectx);
-	EVP_CIPHER_CTX_cleanup(dctx);
-}
-
 int
-aesenc(EVP_CIPHER_CTX *ctx, unsigned char *ct, unsigned char *pt, int plen)
+aesenc(EVP_CIPHER_CTX *ctx, unsigned char *ct, unsigned char *pt, int plen,
+       unsigned char *key, unsigned char *iv)
 {
-	int clen;
+	int clen, flen;
 
+	if (EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv) != 1)
+		logerr("EVP_EncryptInit_ex failed");
 	if (EVP_EncryptUpdate(ctx, ct, &clen, pt, plen) != 1)
 		logerr("EVP_EncryptUpdate failed");
-	return clen;
+	if (EVP_EncryptFinal_ex(ctx, ct + clen, &flen) != 1)
+		logerr("EVP_EncryptFinal_ex failed");
+	return clen + flen;
 }
 
 int
-aesdec(EVP_CIPHER_CTX *ctx, unsigned char *pt, unsigned char *ct, int clen)
+aesdec(EVP_CIPHER_CTX *ctx, unsigned char *pt, unsigned char *ct, int clen,
+       unsigned char *key, unsigned char *iv)
 {
-	int plen;
+	int plen, flen;
 
+	if (EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv) != 1)
+		logerr("EVP_DecryptInit_ex failed");
 	if (EVP_DecryptUpdate(ctx, pt, &plen, ct, clen) != 1)
 		logerr("EVP_DecryptUpdate failed");
-	return plen;
+	if (EVP_DecryptFinal_ex(ctx, pt + plen, &flen) != 1)
+		logerr("EVP_DecryptFinal_ex failed");
+	return plen + flen;
 }
 
 #if defined(__linux__)
@@ -453,18 +430,16 @@ writenet(int fd, unsigned char *pt, int len)
 {
 	unsigned char payload[MTU + AES_BLOCK_SIZE];
 	unsigned char hdr[HDRLEN];
-	unsigned char pkt[sizeof(hdr) + sizeof(payload)];
-
-	/* fill padding with random bytes */
-	if (padlen(len, AES_BLOCK_SIZE) > 0)
-		arc4random_buf(&payload[len], padlen(len, AES_BLOCK_SIZE));
+	unsigned char iv[IVLEN];
+	unsigned char pkt[sizeof(hdr) + sizeof(payload) + sizeof(iv)];
 
+	arc4random_buf(iv, IVLEN);
+	len = aesenc(&ectx, payload, pt, len, aeskey, iv);
 	pack16(hdr, len);
-	len = padto(len, AES_BLOCK_SIZE);
-	aesenc(&enc, payload, pt, len);
 	memcpy(pkt, hdr, HDRLEN);
-	memcpy(&pkt[HDRLEN], payload, len);
-	len += HDRLEN;
+	memcpy(&pkt[HDRLEN], iv, IVLEN);
+	memcpy(&pkt[HDRLEN + IVLEN], payload, len);
+	len += IVLEN + HDRLEN;
 	return writeall(fd, pkt, len);
 }
 
@@ -473,38 +448,43 @@ readnet(int fd, unsigned char *pt, int len)
 {
 	unsigned char payload[MTU + AES_BLOCK_SIZE];
 	unsigned char hdr[HDRLEN];
-	int n, pktlen, paddedlen;
+	unsigned char iv[IVLEN];
+	int n, pktlen;
 
-	/* read unpadded packet length */
+	/* read packet length */
 	n = readall(fd, hdr, HDRLEN);
 	if (n <= 0)
 		return n;
 
 	pktlen = unpack16(hdr);
 	pktlen &= PKTLENMASK;
-	paddedlen = padto(pktlen, AES_BLOCK_SIZE);
+
+	/* read IV */
+	n = readall(fd, iv, IVLEN);
+	if (n <= 0)
+		return n;
 
 	/* discard bad packets */
-	if (pktlen > len || paddedlen > sizeof(payload)) {
-		while (paddedlen) {
-			if (paddedlen > sizeof(payload))
+	if (pktlen > sizeof(payload)) {
+		while (pktlen) {
+			if (pktlen > sizeof(payload))
 				len = sizeof(payload);
 			else
-				len = paddedlen;
+				len = pktlen;
 			n = readall(fd, payload, len);
 			if (n <= 0)
 				break;
-			paddedlen -= n;
+			pktlen -= n;
 		}
 		return BADPKT;
 	}
 
 	/* read encrypted payload */
-	n = readall(fd, payload, paddedlen);
+	n = readall(fd, payload, pktlen);
 	if (n <= 0)
 		return n;
 
-	aesdec(&dec, pt, payload, paddedlen);
+	pktlen = aesdec(&dctx, pt, payload, pktlen, aeskey, iv);
 	return pktlen;
 }
 
@@ -670,7 +650,6 @@ serversetup(int devfd)
 
 		setsockopt(netfd, IPPROTO_TCP, TCP_NODELAY, (int []){1}, sizeof(int));
 
-		aesinit(&enc, &dec);
 		ret = challenge(netfd);
 		if (ret < 0) {
 			logwarn("server -> client challenge failed");
@@ -685,7 +664,6 @@ serversetup(int devfd)
 
 		tunnel(netfd, devfd);
 err:
-		aesterm(&enc, &dec);
 		close(netfd);
 		if (debug)
 			logdbg("remote peer disconnected: %s",
@@ -729,7 +707,6 @@ clientsetup(int devfd)
 	if (debug)
 		logdbg("connected to %s:%s", host, port);
 
-	aesinit(&enc, &dec);
 	ret = response(netfd);
 	if (ret < 0) {
 		logwarn("client -> server response failed");
@@ -745,7 +722,6 @@ clientsetup(int devfd)
 	ret = tunnel(netfd, devfd);
 err:
 	logwarn("connection to %s:%s dropped", host, port);
-	aesterm(&enc, &dec);
 	close(netfd);
 	return ret;
 }
@@ -837,6 +813,7 @@ main(int argc, char *argv[])
 	prepkey((unsigned char *)pw, strlen(pw));
 	explicit_bzero(pw, strlen(pw));
 
+	aesinit(&ectx, &dctx);
 	if (sflag)
 		return serversetup(devfd);
 	revokeprivs();