changeset 1672:3a97f14c0235

Add Chacha20-Poly1305, AES128-GCM and AES256-GCM support (#93) * Add Chacha20-Poly1305 authenticated encryption * Add general AEAD approach. * Add [email protected] algo using LibTomCrypt chacha and poly1305 routines. Chacha20-Poly1305 is generally faster than AES256 on CPU w/o dedicated AES instructions, having the same key size. Compiling in will add ~5,5kB to binary size on x86-64. function old new delta chacha_crypt - 1397 +1397 _poly1305_block - 608 +608 poly1305_done - 595 +595 dropbear_chachapoly_crypt - 457 +457 .rodata 26976 27392 +416 poly1305_process - 290 +290 poly1305_init - 221 +221 chacha_setup - 218 +218 encrypt_packet 1068 1270 +202 dropbear_chachapoly_getlength - 147 +147 decrypt_packet 756 897 +141 chacha_ivctr64 - 137 +137 read_packet 543 637 +94 dropbear_chachapoly_start - 94 +94 read_kex_algos 792 880 +88 chacha_keystream - 69 +69 dropbear_mode_chachapoly - 48 +48 sshciphers 280 320 +40 dropbear_mode_none 24 48 +24 dropbear_mode_ctr 24 48 +24 dropbear_mode_cbc 24 48 +24 dropbear_chachapoly_mac - 24 +24 dropbear_chachapoly - 24 +24 gen_new_keys 848 854 +6 ------------------------------------------------------------------------------ (add/remove: 14/0 grow/shrink: 10/0 up/down: 5388/0) Total: 5388 bytes * Add AES128-GCM and AES256-GCM authenticated encryption * Add general AES-GCM mode. * Add [email protected] and [email protected] algo using LibTomCrypt gcm routines. AES-GCM is combination of AES CTR mode and GHASH, slower than AES-CTR on CPU w/o dedicated AES/GHASH instructions therefore disabled by default. Compiling in will add ~6kB to binary size on x86-64. function old new delta gcm_process - 1060 +1060 .rodata 26976 27808 +832 gcm_gf_mult - 820 +820 gcm_add_aad - 660 +660 gcm_shift_table - 512 +512 gcm_done - 471 +471 gcm_add_iv - 384 +384 gcm_init - 347 +347 dropbear_gcm_crypt - 309 +309 encrypt_packet 1068 1270 +202 decrypt_packet 756 897 +141 gcm_reset - 118 +118 read_packet 543 637 +94 read_kex_algos 792 880 +88 sshciphers 280 360 +80 gcm_mult_h - 80 +80 dropbear_gcm_start - 62 +62 dropbear_mode_gcm - 48 +48 dropbear_mode_none 24 48 +24 dropbear_mode_ctr 24 48 +24 dropbear_mode_cbc 24 48 +24 dropbear_ghash - 24 +24 dropbear_gcm_getlength - 24 +24 gen_new_keys 848 854 +6 ------------------------------------------------------------------------------ (add/remove: 14/0 grow/shrink: 10/0 up/down: 6434/0) Total: 6434 bytes
author Vladislav Grishenko <themiron@users.noreply.github.com>
date Mon, 25 May 2020 20:50:25 +0500
parents 5c8913b7464c
children e0871128e61f
files Makefile.in algo.h chachapoly.c chachapoly.h common-algo.c common-kex.c default_options.h gcm.c gcm.h libtomcrypt/src/headers/tomcrypt_dropbear.h packet.c session.h sysoptions.h
diffstat 13 files changed, 559 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.in	Mon May 25 18:28:27 2020 +0500
+++ b/Makefile.in	Mon May 25 20:50:25 2020 +0500
@@ -53,7 +53,7 @@
 CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \
 			common-channel.o common-chansession.o termcodes.o loginrec.o \
 			tcp-accept.o listener.o process-packet.o dh_groups.o \
-			common-runopts.o circbuffer.o list.o netio.o
+			common-runopts.o circbuffer.o list.o netio.o chachapoly.o gcm.o
 
 KEYOBJS=dropbearkey.o
 
--- a/algo.h	Mon May 25 18:28:27 2020 +0500
+++ b/algo.h	Mon May 25 20:50:25 2020 +0500
@@ -72,6 +72,14 @@
 			unsigned long len, void *cipher_state);
 	int (*decrypt)(const unsigned char *ct, unsigned char *pt, 
 			unsigned long len, void *cipher_state);
+	int (*aead_crypt)(unsigned int seq,
+			const unsigned char *in, unsigned char *out,
+			unsigned long len, unsigned long taglen,
+			void *cipher_state, int direction);
+	int (*aead_getlength)(unsigned int seq,
+			const unsigned char *in, unsigned int *outlen,
+			unsigned long len, void *cipher_state);
+	const struct dropbear_hash *aead_mac;
 };
 
 struct dropbear_hash {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/chachapoly.c	Mon May 25 20:50:25 2020 +0500
@@ -0,0 +1,148 @@
+/*
+ * Dropbear SSH
+ * 
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2020 by Vladislav Grishenko
+ * All rights reserved.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE. */
+
+#include "includes.h"
+#include "algo.h"
+#include "dbutil.h"
+#include "chachapoly.h"
+
+#if DROPBEAR_CHACHA20POLY1305
+
+#define CHACHA20_KEY_LEN 32
+#define CHACHA20_BLOCKSIZE 8
+#define POLY1305_KEY_LEN 32
+#define POLY1305_TAG_LEN 16
+
+static const struct ltc_cipher_descriptor dummy = {.name = NULL};
+
+static const struct dropbear_hash dropbear_chachapoly_mac =
+	{NULL, POLY1305_KEY_LEN, POLY1305_TAG_LEN};
+
+const struct dropbear_cipher dropbear_chachapoly =
+	{&dummy, CHACHA20_KEY_LEN*2, CHACHA20_BLOCKSIZE};
+
+static int dropbear_chachapoly_start(int UNUSED(cipher), const unsigned char* UNUSED(IV),
+			const unsigned char *key, int keylen,
+			int UNUSED(num_rounds), dropbear_chachapoly_state *state) {
+	int err;
+
+	TRACE2(("enter dropbear_chachapoly_start"))
+
+	if (keylen != CHACHA20_KEY_LEN*2) {
+		return CRYPT_ERROR;
+	}
+
+	if ((err = chacha_setup(&state->chacha, key,
+				CHACHA20_KEY_LEN, 20)) != CRYPT_OK) {
+		return err;
+	}
+
+	if ((err = chacha_setup(&state->header, key + CHACHA20_KEY_LEN,
+				CHACHA20_KEY_LEN, 20) != CRYPT_OK)) {
+		return err;
+	}
+
+	TRACE2(("leave dropbear_chachapoly_start"))
+	return CRYPT_OK;
+}
+
+static int dropbear_chachapoly_crypt(unsigned int seq,
+			const unsigned char *in, unsigned char *out,
+			unsigned long len, unsigned long taglen,
+			dropbear_chachapoly_state *state, int direction) {
+	poly1305_state poly;
+	unsigned char seqbuf[8], key[POLY1305_KEY_LEN], tag[POLY1305_TAG_LEN];
+	int err;
+
+	TRACE2(("enter dropbear_chachapoly_crypt"))
+
+	if (len < 4 || taglen != POLY1305_TAG_LEN) {
+		return CRYPT_ERROR;
+	}
+
+	STORE64H(seq, seqbuf);
+	chacha_ivctr64(&state->chacha, seqbuf, sizeof(seqbuf), 0);
+	if ((err = chacha_keystream(&state->chacha, key, sizeof(key))) != CRYPT_OK) {
+		return err;
+	}
+
+	poly1305_init(&poly, key, sizeof(key));
+	if (direction == LTC_DECRYPT) {
+		poly1305_process(&poly, in, len);
+		poly1305_done(&poly, tag, &taglen);
+		if (constant_time_memcmp(in + len, tag, taglen) != 0) {
+			return CRYPT_ERROR;
+		}
+	}
+
+	chacha_ivctr64(&state->header, seqbuf, sizeof(seqbuf), 0);
+	if ((err = chacha_crypt(&state->header, in, 4, out)) != CRYPT_OK) {
+		return err;
+	}
+
+	chacha_ivctr64(&state->chacha, seqbuf, sizeof(seqbuf), 1);
+	if ((err = chacha_crypt(&state->chacha, in + 4, len - 4, out + 4)) != CRYPT_OK) {
+		return err;
+	}
+
+	if (direction == LTC_ENCRYPT) {
+		poly1305_process(&poly, out, len);
+		poly1305_done(&poly, out + len, &taglen);
+	}
+
+	TRACE2(("leave dropbear_chachapoly_crypt"))
+	return CRYPT_OK;
+}
+
+static int dropbear_chachapoly_getlength(unsigned int seq,
+			const unsigned char *in, unsigned int *outlen,
+			unsigned long len, dropbear_chachapoly_state *state) {
+	unsigned char seqbuf[8], buf[4];
+	int err;
+
+	TRACE2(("enter dropbear_chachapoly_parse"))
+
+	if (len < sizeof(buf)) {
+		return CRYPT_ERROR;
+	}
+
+	STORE64H(seq, seqbuf);
+	chacha_ivctr64(&state->header, seqbuf, sizeof(seqbuf), 0);
+	if ((err = chacha_crypt(&state->header, in, sizeof(buf), buf)) != CRYPT_OK) {
+		return err;
+	}
+
+	LOAD32H(*outlen, buf);
+
+	TRACE2(("leave dropbear_chachapoly_parse"))
+	return CRYPT_OK;
+}
+
+const struct dropbear_cipher_mode dropbear_mode_chachapoly =
+	{(void *)dropbear_chachapoly_start, NULL, NULL,
+	 (void *)dropbear_chachapoly_crypt,
+	 (void *)dropbear_chachapoly_getlength, &dropbear_chachapoly_mac};
+
+#endif /* DROPBEAR_CHACHA20POLY1305 */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/chachapoly.h	Mon May 25 20:50:25 2020 +0500
@@ -0,0 +1,44 @@
+/*
+ * Dropbear SSH
+ * 
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2020 by Vladislav Grishenko
+ * All rights reserved.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE. */
+
+#ifndef DROPBEAR_DROPBEAR_CHACHAPOLY_H_
+#define DROPBEAR_DROPBEAR_CHACHAPOLY_H_
+
+#include "includes.h"
+#include "algo.h"
+
+#if DROPBEAR_CHACHA20POLY1305
+
+typedef struct {
+	chacha_state chacha;
+	chacha_state header;
+} dropbear_chachapoly_state;
+
+extern const struct dropbear_cipher dropbear_chachapoly;
+extern const struct dropbear_cipher_mode dropbear_mode_chachapoly;
+
+#endif /* DROPBEAR_CHACHA20POLY1305 */
+
+#endif /* DROPBEAR_DROPBEAR_CHACHAPOLY_H_ */
--- a/common-algo.c	Mon May 25 18:28:27 2020 +0500
+++ b/common-algo.c	Mon May 25 20:50:25 2020 +0500
@@ -30,6 +30,8 @@
 #include "dh_groups.h"
 #include "ltc_prng.h"
 #include "ecc.h"
+#include "gcm.h"
+#include "chachapoly.h"
 
 /* This file (algo.c) organises the ciphers which can be used, and is used to
  * decide which ciphers/hashes/compression/signing to use during key exchange*/
@@ -86,11 +88,11 @@
  * about the symmetric_CBC vs symmetric_CTR cipher_state pointer */
 #if DROPBEAR_ENABLE_CBC_MODE
 const struct dropbear_cipher_mode dropbear_mode_cbc =
-	{(void*)cbc_start, (void*)cbc_encrypt, (void*)cbc_decrypt};
+	{(void*)cbc_start, (void*)cbc_encrypt, (void*)cbc_decrypt, NULL, NULL, NULL};
 #endif /* DROPBEAR_ENABLE_CBC_MODE */
 
 const struct dropbear_cipher_mode dropbear_mode_none =
-	{void_start, void_cipher, void_cipher};
+	{void_start, void_cipher, void_cipher, NULL, NULL, NULL};
 
 #if DROPBEAR_ENABLE_CTR_MODE
 /* a wrapper to make ctr_start and cbc_start look the same */
@@ -101,7 +103,7 @@
 	return ctr_start(cipher, IV, key, keylen, num_rounds, CTR_COUNTER_BIG_ENDIAN, ctr);
 }
 const struct dropbear_cipher_mode dropbear_mode_ctr =
-	{(void*)dropbear_big_endian_ctr_start, (void*)ctr_encrypt, (void*)ctr_decrypt};
+	{(void*)dropbear_big_endian_ctr_start, (void*)ctr_encrypt, (void*)ctr_decrypt, NULL, NULL, NULL};
 #endif /* DROPBEAR_ENABLE_CTR_MODE */
 
 /* Mapping of ssh hashes to libtomcrypt hashes, including keysize etc.
@@ -137,6 +139,19 @@
  * that is also supported by the server will get used. */
 
 algo_type sshciphers[] = {
+#if DROPBEAR_CHACHA20POLY1305
+	{"[email protected]", 0, &dropbear_chachapoly, 1, &dropbear_mode_chachapoly},
+#endif
+
+#if DROPBEAR_ENABLE_GCM_MODE
+#if DROPBEAR_AES128
+	{"[email protected]", 0, &dropbear_aes128, 1, &dropbear_mode_gcm},
+#endif
+#if DROPBEAR_AES256
+	{"[email protected]", 0, &dropbear_aes256, 1, &dropbear_mode_gcm},
+#endif
+#endif /* DROPBEAR_ENABLE_GCM_MODE */
+
 #if DROPBEAR_ENABLE_CTR_MODE
 #if DROPBEAR_AES128
 	{"aes128-ctr", 0, &dropbear_aes128, 1, &dropbear_mode_ctr},
--- a/common-kex.c	Mon May 25 18:28:27 2020 +0500
+++ b/common-kex.c	Mon May 25 20:50:25 2020 +0500
@@ -329,9 +329,12 @@
 	hashkeys(S2C_key, sizeof(S2C_key), &hs, 'D');
 
 	if (ses.newkeys->recv.algo_crypt->cipherdesc != NULL) {
-		int recv_cipher = find_cipher(ses.newkeys->recv.algo_crypt->cipherdesc->name);
-		if (recv_cipher < 0)
-			dropbear_exit("Crypto error");
+		int recv_cipher = -1;
+		if (ses.newkeys->recv.algo_crypt->cipherdesc->name != NULL) {
+			recv_cipher = find_cipher(ses.newkeys->recv.algo_crypt->cipherdesc->name);
+			if (recv_cipher < 0)
+				dropbear_exit("Crypto error");
+		}
 		if (ses.newkeys->recv.crypt_mode->start(recv_cipher, 
 				recv_IV, recv_key, 
 				ses.newkeys->recv.algo_crypt->keysize, 0, 
@@ -341,9 +344,12 @@
 	}
 
 	if (ses.newkeys->trans.algo_crypt->cipherdesc != NULL) {
-		int trans_cipher = find_cipher(ses.newkeys->trans.algo_crypt->cipherdesc->name);
-		if (trans_cipher < 0)
-			dropbear_exit("Crypto error");
+		int trans_cipher = -1;
+		if (ses.newkeys->trans.algo_crypt->cipherdesc->name != NULL) {
+			trans_cipher = find_cipher(ses.newkeys->trans.algo_crypt->cipherdesc->name);
+			if (trans_cipher < 0)
+				dropbear_exit("Crypto error");
+		}
 		if (ses.newkeys->trans.crypt_mode->start(trans_cipher, 
 				trans_IV, trans_key, 
 				ses.newkeys->trans.algo_crypt->keysize, 0, 
@@ -868,19 +874,29 @@
 
 	/* mac_algorithms_client_to_server */
 	c2s_hash_algo = buf_match_algo(ses.payload, sshhashes, NULL, NULL);
+#if DROPBEAR_AEAD_MODE
+	if (((struct dropbear_cipher_mode*)c2s_cipher_algo->mode)->aead_crypt != NULL) {
+		c2s_hash_algo = NULL;
+	} else
+#endif
 	if (c2s_hash_algo == NULL) {
 		erralgo = "mac c->s";
 		goto error;
 	}
-	TRACE(("hash c2s is  %s", c2s_hash_algo->name))
+	TRACE(("hash c2s is  %s", c2s_hash_algo ? c2s_hash_algo->name : "<implicit>"))
 
 	/* mac_algorithms_server_to_client */
 	s2c_hash_algo = buf_match_algo(ses.payload, sshhashes, NULL, NULL);
+#if DROPBEAR_AEAD_MODE
+	if (((struct dropbear_cipher_mode*)s2c_cipher_algo->mode)->aead_crypt != NULL) {
+		s2c_hash_algo = NULL;
+	} else
+#endif
 	if (s2c_hash_algo == NULL) {
 		erralgo = "mac s->c";
 		goto error;
 	}
-	TRACE(("hash s2c is  %s", s2c_hash_algo->name))
+	TRACE(("hash s2c is  %s", s2c_hash_algo ? s2c_hash_algo->name : "<implicit>"))
 
 	/* compression_algorithms_client_to_server */
 	c2s_comp_algo = buf_match_algo(ses.payload, ses.compress_algos, NULL, NULL);
@@ -925,8 +941,14 @@
 		ses.newkeys->trans.crypt_mode =
 			(struct dropbear_cipher_mode*)c2s_cipher_algo->mode;
 		ses.newkeys->recv.algo_mac = 
+#if DROPBEAR_AEAD_MODE
+			s2c_hash_algo == NULL ? ses.newkeys->recv.crypt_mode->aead_mac :
+#endif
 			(struct dropbear_hash*)s2c_hash_algo->data;
 		ses.newkeys->trans.algo_mac = 
+#if DROPBEAR_AEAD_MODE
+			c2s_hash_algo == NULL ? ses.newkeys->trans.crypt_mode->aead_mac :
+#endif
 			(struct dropbear_hash*)c2s_hash_algo->data;
 		ses.newkeys->recv.algo_comp = s2c_comp_algo->val;
 		ses.newkeys->trans.algo_comp = c2s_comp_algo->val;
@@ -941,8 +963,14 @@
 		ses.newkeys->trans.crypt_mode =
 			(struct dropbear_cipher_mode*)s2c_cipher_algo->mode;
 		ses.newkeys->recv.algo_mac = 
+#if DROPBEAR_AEAD_MODE
+			c2s_hash_algo == NULL ? ses.newkeys->recv.crypt_mode->aead_mac :
+#endif
 			(struct dropbear_hash*)c2s_hash_algo->data;
 		ses.newkeys->trans.algo_mac = 
+#if DROPBEAR_AEAD_MODE
+			s2c_hash_algo == NULL ? ses.newkeys->trans.crypt_mode->aead_mac :
+#endif
 			(struct dropbear_hash*)s2c_hash_algo->data;
 		ses.newkeys->recv.algo_comp = c2s_comp_algo->val;
 		ses.newkeys->trans.algo_comp = s2c_comp_algo->val;
--- a/default_options.h	Mon May 25 18:28:27 2020 +0500
+++ b/default_options.h	Mon May 25 20:50:25 2020 +0500
@@ -99,6 +99,19 @@
  * and forwards compatibility */
 #define DROPBEAR_ENABLE_CTR_MODE 1
 
+/* Enable "Galois/Counter Mode" for ciphers. This authenticated
+ * encryption mode is combination of CTR mode and GHASH. Recommended
+ * for security and forwards compatibility, but slower than CTR on
+ * CPU w/o dedicated AES/GHASH instructions.
+ * Compiling in will add ~6kB to binary size on x86-64 */
+#define DROPBEAR_ENABLE_GCM_MODE 0
+
+/* Enable Chacha20-Poly1305 authenticated encryption mode. This is
+ * generally faster than AES256 on CPU w/o dedicated AES instructions,
+ * having the same key size.
+ * Compiling in will add ~5,5kB to binary size on x86-64 */
+#define DROPBEAR_CHACHA20POLY1305 1
+
 /* Message integrity. sha2-256 is recommended as a default, 
    sha1 for compatibility */
 #define DROPBEAR_SHA1_HMAC 1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gcm.c	Mon May 25 20:50:25 2020 +0500
@@ -0,0 +1,120 @@
+/*
+ * Dropbear SSH
+ * 
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2020 by Vladislav Grishenko
+ * All rights reserved.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE. */
+
+#include "includes.h"
+#include "algo.h"
+#include "dbutil.h"
+#include "gcm.h"
+
+#if DROPBEAR_ENABLE_GCM_MODE
+
+#define GHASH_LEN 16
+
+static const struct dropbear_hash dropbear_ghash =
+	{NULL, 0, GHASH_LEN};
+
+static int dropbear_gcm_start(int cipher, const unsigned char *IV,
+			const unsigned char *key, int keylen,
+			int UNUSED(num_rounds), dropbear_gcm_state *state) {
+	int err;
+
+	TRACE2(("enter dropbear_gcm_start"))
+
+	if ((err = gcm_init(&state->gcm, cipher, key, keylen)) != CRYPT_OK) {
+		return err;
+	}
+	memcpy(state->iv, IV, GCM_NONCE_LEN);
+
+	TRACE2(("leave dropbear_gcm_start"))
+	return CRYPT_OK;
+}
+
+static int dropbear_gcm_crypt(unsigned int UNUSED(seq),
+			const unsigned char *in, unsigned char *out,
+			unsigned long len, unsigned long taglen,
+			dropbear_gcm_state *state, int direction) {
+	unsigned char *iv, tag[GHASH_LEN];
+	int i, err;
+
+	TRACE2(("enter dropbear_gcm_crypt"))
+
+	if (len < 4 || taglen != GHASH_LEN) {
+		return CRYPT_ERROR;
+	}
+
+	gcm_reset(&state->gcm);
+
+	if ((err = gcm_add_iv(&state->gcm,
+				state->iv, GCM_NONCE_LEN)) != CRYPT_OK) {
+		return err;
+	}
+
+	if ((err = gcm_add_aad(&state->gcm, in, 4)) != CRYPT_OK) {
+		return err;
+	}
+
+	if ((err = gcm_process(&state->gcm, (unsigned char *) in + 4,
+				len - 4, out + 4, direction)) != CRYPT_OK) {
+		return err;
+	}
+
+	if (direction == LTC_ENCRYPT) {
+		gcm_done(&state->gcm, out + len, &taglen);
+	} else {
+		gcm_done(&state->gcm, tag, &taglen);
+		if (constant_time_memcmp(in + len, tag, taglen) != 0) {
+			return CRYPT_ERROR;
+		}
+	}
+
+	/* increment invocation counter */
+	iv = state->iv + GCM_IVFIX_LEN;
+	for (i = GCM_IVCTR_LEN - 1; i >= 0 && ++iv[i] == 0; i--);
+
+	TRACE2(("leave dropbear_gcm_crypt"))
+	return CRYPT_OK;
+}
+
+static int dropbear_gcm_getlength(unsigned int UNUSED(seq),
+			const unsigned char *in, unsigned int *outlen,
+			unsigned long len, dropbear_gcm_state* UNUSED(state)) {
+	TRACE2(("enter dropbear_gcm_parse"))
+
+	if (len < 4) {
+		return CRYPT_ERROR;
+	}
+
+	LOAD32H(*outlen, in);
+
+	TRACE2(("leave dropbear_gcm_parse"))
+	return CRYPT_OK;
+}
+
+const struct dropbear_cipher_mode dropbear_mode_gcm =
+	{(void *)dropbear_gcm_start, NULL, NULL,
+	 (void *)dropbear_gcm_crypt,
+	 (void *)dropbear_gcm_getlength, &dropbear_ghash};
+
+#endif /* DROPBEAR_ENABLE_GCM_MODE */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gcm.h	Mon May 25 20:50:25 2020 +0500
@@ -0,0 +1,47 @@
+/*
+ * Dropbear SSH
+ * 
+ * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2020 by Vladislav Grishenko
+ * All rights reserved.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE. */
+
+#ifndef DROPBEAR_DROPBEAR_GCM_H_
+#define DROPBEAR_DROPBEAR_GCM_H_
+
+#include "includes.h"
+#include "algo.h"
+
+#if DROPBEAR_ENABLE_GCM_MODE
+
+#define GCM_IVFIX_LEN 4
+#define GCM_IVCTR_LEN 8
+#define GCM_NONCE_LEN (GCM_IVFIX_LEN + GCM_IVCTR_LEN)
+
+typedef struct {
+	gcm_state gcm;
+	unsigned char iv[GCM_NONCE_LEN];
+} dropbear_gcm_state;
+
+extern const struct dropbear_cipher_mode dropbear_mode_gcm;
+
+#endif /* DROPBEAR_ENABLE_GCM_MODE */
+
+#endif /* DROPBEAR_DROPBEAR_GCM_H_ */
--- a/libtomcrypt/src/headers/tomcrypt_dropbear.h	Mon May 25 18:28:27 2020 +0500
+++ b/libtomcrypt/src/headers/tomcrypt_dropbear.h	Mon May 25 20:50:25 2020 +0500
@@ -35,6 +35,14 @@
 #define LTC_CTR_MODE
 #endif
 
+#if DROPBEAR_ENABLE_GCM_MODE
+#define LTC_GCM_MODE
+#endif
+
+#if DROPBEAR_CHACHA20POLY1305
+#define LTC_CHACHA
+#define LTC_POLY1305
+#endif
 
 #if DROPBEAR_SHA512
 #define LTC_SHA512
--- a/packet.c	Mon May 25 18:28:27 2020 +0500
+++ b/packet.c	Mon May 25 20:50:25 2020 +0500
@@ -215,7 +215,7 @@
 
 	unsigned int maxlen;
 	int slen;
-	unsigned int len;
+	unsigned int len, plen;
 	unsigned int blocksize;
 	unsigned int macsize;
 
@@ -254,21 +254,35 @@
 	/* now we have the first block, need to get packet length, so we decrypt
 	 * the first block (only need first 4 bytes) */
 	buf_setpos(ses.readbuf, 0);
-	if (ses.keys->recv.crypt_mode->decrypt(buf_getptr(ses.readbuf, blocksize), 
-				buf_getwriteptr(ses.readbuf, blocksize),
-				blocksize,
-				&ses.keys->recv.cipher_state) != CRYPT_OK) {
-		dropbear_exit("Error decrypting");
+#if DROPBEAR_AEAD_MODE
+	if (ses.keys->recv.crypt_mode->aead_crypt) {
+		if (ses.keys->recv.crypt_mode->aead_getlength(ses.recvseq,
+					buf_getptr(ses.readbuf, blocksize), &plen,
+					blocksize,
+					&ses.keys->recv.cipher_state) != CRYPT_OK) {
+			dropbear_exit("Error decrypting");
+		}
+		len = plen + 4 + macsize;
+	} else
+#endif
+	{
+		if (ses.keys->recv.crypt_mode->decrypt(buf_getptr(ses.readbuf, blocksize), 
+					buf_getwriteptr(ses.readbuf, blocksize),
+					blocksize,
+					&ses.keys->recv.cipher_state) != CRYPT_OK) {
+			dropbear_exit("Error decrypting");
+		}
+		plen = buf_getint(ses.readbuf) + 4;
+		len = plen + macsize;
 	}
-	len = buf_getint(ses.readbuf) + 4 + macsize;
 
 	TRACE2(("packet size is %u, block %u mac %u", len, blocksize, macsize))
 
 
 	/* check packet length */
 	if ((len > RECV_MAX_PACKET_LEN) ||
-		(len < MIN_PACKET_LEN + macsize) ||
-		((len - macsize) % blocksize != 0)) {
+		(plen < blocksize) ||
+		(plen % blocksize != 0)) {
 		dropbear_exit("Integrity error (bad packet size %u)", len);
 	}
 
@@ -294,23 +308,42 @@
 
 	ses.kexstate.datarecv += ses.readbuf->len;
 
-	/* we've already decrypted the first blocksize in read_packet_init */
-	buf_setpos(ses.readbuf, blocksize);
+#if DROPBEAR_AEAD_MODE
+	if (ses.keys->recv.crypt_mode->aead_crypt) {
+		/* first blocksize is not decrypted yet */
+		buf_setpos(ses.readbuf, 0);
 
-	/* decrypt it in-place */
-	len = ses.readbuf->len - macsize - ses.readbuf->pos;
-	if (ses.keys->recv.crypt_mode->decrypt(
-				buf_getptr(ses.readbuf, len), 
-				buf_getwriteptr(ses.readbuf, len),
-				len,
-				&ses.keys->recv.cipher_state) != CRYPT_OK) {
-		dropbear_exit("Error decrypting");
-	}
-	buf_incrpos(ses.readbuf, len);
+		/* decrypt it in-place */
+		len = ses.readbuf->len - macsize - ses.readbuf->pos;
+		if (ses.keys->recv.crypt_mode->aead_crypt(ses.recvseq,
+					buf_getptr(ses.readbuf, len + macsize),
+					buf_getwriteptr(ses.readbuf, len),
+					len, macsize,
+					&ses.keys->recv.cipher_state, LTC_DECRYPT) != CRYPT_OK) {
+			dropbear_exit("Error decrypting");
+		}
+		buf_incrpos(ses.readbuf, len);
+	} else
+#endif
+	{
+		/* we've already decrypted the first blocksize in read_packet_init */
+		buf_setpos(ses.readbuf, blocksize);
 
-	/* check the hmac */
-	if (checkmac() != DROPBEAR_SUCCESS) {
-		dropbear_exit("Integrity error");
+		/* decrypt it in-place */
+		len = ses.readbuf->len - macsize - ses.readbuf->pos;
+		if (ses.keys->recv.crypt_mode->decrypt(
+					buf_getptr(ses.readbuf, len), 
+					buf_getwriteptr(ses.readbuf, len),
+					len,
+					&ses.keys->recv.cipher_state) != CRYPT_OK) {
+			dropbear_exit("Error decrypting");
+		}
+		buf_incrpos(ses.readbuf, len);
+
+		/* check the hmac */
+		if (checkmac() != DROPBEAR_SUCCESS) {
+			dropbear_exit("Integrity error");
+		}
 	}
 
 	/* get padding length */
@@ -557,9 +590,16 @@
 	buf_setpos(ses.writepayload, 0);
 	buf_setlen(ses.writepayload, 0);
 
-	/* length of padding - packet length must be a multiple of blocksize,
-	 * with a minimum of 4 bytes of padding */
-	padlen = blocksize - (writebuf->len) % blocksize;
+	/* length of padding - packet length excluding the packetlength uint32
+	 * field in aead mode must be a multiple of blocksize, with a minimum of
+	 * 4 bytes of padding */
+	len = writebuf->len;
+#if DROPBEAR_AEAD_MODE
+	if (ses.keys->trans.crypt_mode->aead_crypt) {
+		len -= 4;
+	}
+#endif
+	padlen = blocksize - len % blocksize;
 	if (padlen < 4) {
 		padlen += blocksize;
 	}
@@ -579,23 +619,42 @@
 	buf_incrlen(writebuf, padlen);
 	genrandom(buf_getptr(writebuf, padlen), padlen);
 
-	make_mac(ses.transseq, &ses.keys->trans, writebuf, writebuf->len, mac_bytes);
+#if DROPBEAR_AEAD_MODE
+	if (ses.keys->trans.crypt_mode->aead_crypt) {
+		/* do the actual encryption, in-place */
+		buf_setpos(writebuf, 0);
+		/* encrypt it in-place*/
+		len = writebuf->len;
+		buf_incrlen(writebuf, mac_size);
+		if (ses.keys->trans.crypt_mode->aead_crypt(ses.transseq,
+					buf_getptr(writebuf, len),
+					buf_getwriteptr(writebuf, len + mac_size),
+					len, mac_size,
+					&ses.keys->trans.cipher_state, LTC_ENCRYPT) != CRYPT_OK) {
+			dropbear_exit("Error encrypting");
+		}
+		buf_incrpos(writebuf, len + mac_size);
+	} else
+#endif
+	{
+		make_mac(ses.transseq, &ses.keys->trans, writebuf, writebuf->len, mac_bytes);
 
-	/* do the actual encryption, in-place */
-	buf_setpos(writebuf, 0);
-	/* encrypt it in-place*/
-	len = writebuf->len;
-	if (ses.keys->trans.crypt_mode->encrypt(
-				buf_getptr(writebuf, len),
-				buf_getwriteptr(writebuf, len),
-				len,
-				&ses.keys->trans.cipher_state) != CRYPT_OK) {
-		dropbear_exit("Error encrypting");
+		/* do the actual encryption, in-place */
+		buf_setpos(writebuf, 0);
+		/* encrypt it in-place*/
+		len = writebuf->len;
+		if (ses.keys->trans.crypt_mode->encrypt(
+					buf_getptr(writebuf, len),
+					buf_getwriteptr(writebuf, len),
+					len,
+					&ses.keys->trans.cipher_state) != CRYPT_OK) {
+			dropbear_exit("Error encrypting");
+		}
+		buf_incrpos(writebuf, len);
+
+		/* stick the MAC on it */
+		buf_putbytes(writebuf, mac_bytes, mac_size);
 	}
-	buf_incrpos(writebuf, len);
-
-	/* stick the MAC on it */
-	buf_putbytes(writebuf, mac_bytes, mac_size);
 
 	/* Update counts */
 	ses.kexstate.datatrans += writebuf->len;
--- a/session.h	Mon May 25 18:28:27 2020 +0500
+++ b/session.h	Mon May 25 20:50:25 2020 +0500
@@ -41,6 +41,8 @@
 #if DROPBEAR_PLUGIN
 #include "pubkeyapi.h"
 #endif
+#include "gcm.h"
+#include "chachapoly.h"
 
 void common_session_init(int sock_in, int sock_out);
 void session_loop(void(*loophandler)(void)) ATTRIB_NORETURN;
@@ -81,6 +83,12 @@
 #if DROPBEAR_ENABLE_CTR_MODE
 		symmetric_CTR ctr;
 #endif
+#if DROPBEAR_ENABLE_GCM_MODE
+		dropbear_gcm_state gcm;
+#endif
+#if DROPBEAR_CHACHA20POLY1305
+		dropbear_chachapoly_state chachapoly;
+#endif
 	} cipher_state;
 	unsigned char mackey[MAX_MAC_LEN];
 	int valid;
--- a/sysoptions.h	Mon May 25 18:28:27 2020 +0500
+++ b/sysoptions.h	Mon May 25 20:50:25 2020 +0500
@@ -92,7 +92,11 @@
 #define MD5_HASH_SIZE 16
 #define MAX_HASH_SIZE 64 /* sha512 */
 
+#if DROPBEAR_CHACHA20POLY1305
+#define MAX_KEY_LEN 64 /* 2 x 256 bits for chacha20 */
+#else
 #define MAX_KEY_LEN 32 /* 256 bits for aes256 etc */
+#endif
 #define MAX_IV_LEN 20 /* must be same as max blocksize,  */
 
 #if DROPBEAR_SHA2_512_HMAC
@@ -207,6 +211,8 @@
 
 #define DROPBEAR_TWOFISH ((DROPBEAR_TWOFISH256) || (DROPBEAR_TWOFISH128))
 
+#define DROPBEAR_AEAD_MODE ((DROPBEAR_CHACHA20POLY1305) || (DROPBEAR_ENABLE_GCM_MODE))
+
 #define DROPBEAR_CLI_ANYTCPFWD ((DROPBEAR_CLI_REMOTETCPFWD) || (DROPBEAR_CLI_LOCALTCPFWD))
 
 #define DROPBEAR_TCP_ACCEPT ((DROPBEAR_CLI_LOCALTCPFWD) || (DROPBEAR_SVR_REMOTETCPFWD))
@@ -249,7 +255,7 @@
 #endif
 
 #if !(DROPBEAR_AES128 || DROPBEAR_3DES || DROPBEAR_AES256 || DROPBEAR_BLOWFISH \
-      || DROPBEAR_TWOFISH256 || DROPBEAR_TWOFISH128)
+      || DROPBEAR_TWOFISH256 || DROPBEAR_TWOFISH128 || DROPBEAR_CHACHA20POLY1305)
 	#error "At least one encryption algorithm must be enabled. AES128 is recommended."
 #endif