summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile.in2
-rw-r--r--algo.h8
-rw-r--r--chachapoly.c148
-rw-r--r--chachapoly.h44
-rw-r--r--common-algo.c21
-rw-r--r--common-kex.c44
-rw-r--r--default_options.h13
-rw-r--r--gcm.c120
-rw-r--r--gcm.h47
-rw-r--r--libtomcrypt/src/headers/tomcrypt_dropbear.h8
-rw-r--r--packet.c145
-rw-r--r--session.h8
-rw-r--r--sysoptions.h8
13 files changed, 560 insertions, 56 deletions
diff --git a/Makefile.in b/Makefile.in
index aaf7b3b..6db9f04 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -53,7 +53,7 @@ CLIOBJS=cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \
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
diff --git a/algo.h b/algo.h
index b12fb94..efd0d73 100644
--- a/algo.h
+++ b/algo.h
@@ -72,6 +72,14 @@ struct dropbear_cipher_mode {
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 {
diff --git a/chachapoly.c b/chachapoly.c
new file mode 100644
index 0000000..8fb06c5
--- /dev/null
+++ b/chachapoly.c
@@ -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 */
diff --git a/chachapoly.h b/chachapoly.h
new file mode 100644
index 0000000..5a7c5b2
--- /dev/null
+++ b/chachapoly.h
@@ -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_ */
diff --git a/common-algo.c b/common-algo.c
index 558aad2..eb1323f 100644
--- a/common-algo.c
+++ b/common-algo.c
@@ -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 @@ const struct dropbear_cipher dropbear_nocipher =
* 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 @@ static int dropbear_big_endian_ctr_start(int cipher,
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 @@ const struct dropbear_hash dropbear_nohash =
* that is also supported by the server will get used. */
algo_type sshciphers[] = {
+#if DROPBEAR_CHACHA20POLY1305
+ {"chacha20-poly1305@openssh.com", 0, &dropbear_chachapoly, 1, &dropbear_mode_chachapoly},
+#endif
+
+#if DROPBEAR_ENABLE_GCM_MODE
+#if DROPBEAR_AES128
+ {"aes128-gcm@openssh.com", 0, &dropbear_aes128, 1, &dropbear_mode_gcm},
+#endif
+#if DROPBEAR_AES256
+ {"aes256-gcm@openssh.com", 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},
diff --git a/common-kex.c b/common-kex.c
index 16b7e27..5e2923f 100644
--- a/common-kex.c
+++ b/common-kex.c
@@ -329,9 +329,12 @@ static void gen_new_keys() {
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 @@ static void gen_new_keys() {
}
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 @@ static void read_kex_algos() {
/* 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 @@ static void read_kex_algos() {
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 @@ static void read_kex_algos() {
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;
diff --git a/default_options.h b/default_options.h
index bafbb07..2ca01b3 100644
--- a/default_options.h
+++ b/default_options.h
@@ -99,6 +99,19 @@ IMPORTANT: Some options will require "make clean" after changes */
* 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
diff --git a/gcm.c b/gcm.c
new file mode 100644
index 0000000..8f5f3d9
--- /dev/null
+++ b/gcm.c
@@ -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 */
diff --git a/gcm.h b/gcm.h
new file mode 100644
index 0000000..58c530a
--- /dev/null
+++ b/gcm.h
@@ -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_ */
diff --git a/libtomcrypt/src/headers/tomcrypt_dropbear.h b/libtomcrypt/src/headers/tomcrypt_dropbear.h
index b0ce45b..ae29475 100644
--- a/libtomcrypt/src/headers/tomcrypt_dropbear.h
+++ b/libtomcrypt/src/headers/tomcrypt_dropbear.h
@@ -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
diff --git a/packet.c b/packet.c
index 9fda0d6..0454726 100644
--- a/packet.c
+++ b/packet.c
@@ -215,7 +215,7 @@ static int read_packet_init() {
unsigned int maxlen;
int slen;
- unsigned int len;
+ unsigned int len, plen;
unsigned int blocksize;
unsigned int macsize;
@@ -254,21 +254,35 @@ static int read_packet_init() {
/* 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 @@ void decrypt_packet() {
ses.kexstate.datarecv += ses.readbuf->len;
- /* we've already decrypted the first blocksize in read_packet_init */
- buf_setpos(ses.readbuf, blocksize);
-
- /* 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);
+#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->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);
+
+ /* 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");
+ /* check the hmac */
+ if (checkmac() != DROPBEAR_SUCCESS) {
+ dropbear_exit("Integrity error");
+ }
}
/* get padding length */
@@ -557,9 +590,16 @@ void encrypt_packet() {
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 @@ void encrypt_packet() {
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");
+ }
+ buf_incrpos(writebuf, len);
- /* 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");
+ /* 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;
diff --git a/session.h b/session.h
index e436882..01416c9 100644
--- a/session.h
+++ b/session.h
@@ -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 @@ struct key_context_directional {
#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;
diff --git a/sysoptions.h b/sysoptions.h
index 2c27caf..2cfdef2 100644
--- a/sysoptions.h
+++ b/sysoptions.h
@@ -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 @@ If you test it please contact the Dropbear author */
#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 @@ If you test it please contact the Dropbear author */
#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