# HG changeset patch # User Matt Johnston # Date 1590423842 -28800 # Node ID 41bf8f216644ca6b587508e9c6ed4f5d3a6cc301 # Parent e0871128e61fb9a734586e3580103bab00c5aa03# Parent a53e7d2d60be5dd1c2d676159563a2f567659bd2 merge rsa-sha256 diff -r a53e7d2d60be -r 41bf8f216644 Makefile.in --- a/Makefile.in Mon May 25 20:23:02 2020 +0800 +++ b/Makefile.in Tue May 26 00:24:02 2020 +0800 @@ -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 diff -r a53e7d2d60be -r 41bf8f216644 algo.h --- a/algo.h Mon May 25 20:23:02 2020 +0800 +++ b/algo.h Tue May 26 00:24:02 2020 +0800 @@ -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 { diff -r a53e7d2d60be -r 41bf8f216644 chachapoly.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chachapoly.c Tue May 26 00:24:02 2020 +0800 @@ -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 -r a53e7d2d60be -r 41bf8f216644 chachapoly.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/chachapoly.h Tue May 26 00:24:02 2020 +0800 @@ -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 -r a53e7d2d60be -r 41bf8f216644 common-algo.c --- a/common-algo.c Mon May 25 20:23:02 2020 +0800 +++ b/common-algo.c Tue May 26 00:24:02 2020 +0800 @@ -30,6 +30,8 @@ #include "dh_groups.h" #include "ltc_prng.h" #include "ecc.h" +#include "gcm.h" +#include "chachapoly.h" #include "ssh.h" /* This file (algo.c) organises the ciphers which can be used, and is used to @@ -87,11 +89,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 */ @@ -102,7 +104,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. @@ -138,6 +140,19 @@ * 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}, @@ -170,12 +185,18 @@ #if DROPBEAR_TWOFISH128 {"twofish128-cbc", 0, &dropbear_twofish128, 1, &dropbear_mode_cbc}, #endif +#endif /* DROPBEAR_ENABLE_CBC_MODE */ + #if DROPBEAR_3DES +#if DROPBEAR_ENABLE_CTR_MODE {"3des-ctr", 0, &dropbear_3des, 1, &dropbear_mode_ctr}, #endif -#if DROPBEAR_3DES +#if DROPBEAR_ENABLE_CBC_MODE {"3des-cbc", 0, &dropbear_3des, 1, &dropbear_mode_cbc}, #endif +#endif /* DROPBEAR_3DES */ + +#if DROPBEAR_ENABLE_CBC_MODE #if DROPBEAR_BLOWFISH {"blowfish-cbc", 0, &dropbear_blowfish, 1, &dropbear_mode_cbc}, #endif @@ -367,7 +388,7 @@ } if (algolist_len > MAX_PROPOSED_ALGO*(MAX_NAME_LEN+1)) { *ret_count = 0; - } + } /* ret_list will contain a list of the strings parsed out. We will have at least one string (even if it's just "") */ @@ -378,7 +399,7 @@ /* someone is trying something strange */ *ret_count = 0; return; - } + } if (algolist[i] == ',') { if (*ret_count >= max_count) { diff -r a53e7d2d60be -r 41bf8f216644 common-kex.c --- a/common-kex.c Mon May 25 20:23:02 2020 +0800 +++ b/common-kex.c Tue May 26 00:24:02 2020 +0800 @@ -332,9 +332,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); + 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, @@ -344,9 +347,12 @@ } if (ses.newkeys->trans.algo_crypt->cipherdesc != NULL) { - int trans_cipher = find_cipher(ses.newkeys->trans.algo_crypt->cipherdesc->name); + 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, @@ -887,19 +893,29 @@ /* mac_algorithms_client_to_server */ c2s_hash_algo = buf_match_algo(ses.payload, sshhashes, 0, 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 : "")) /* mac_algorithms_server_to_client */ s2c_hash_algo = buf_match_algo(ses.payload, sshhashes, 0, 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 : "")) /* compression_algorithms_client_to_server */ c2s_comp_algo = buf_match_algo(ses.payload, ses.compress_algos, 0, NULL); @@ -944,8 +960,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; @@ -960,8 +982,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; diff -r a53e7d2d60be -r 41bf8f216644 default_options.h --- a/default_options.h Mon May 25 20:23:02 2020 +0800 +++ b/default_options.h Tue May 26 00:24:02 2020 +0800 @@ -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 diff -r a53e7d2d60be -r 41bf8f216644 dropbearkey.1 --- a/dropbearkey.1 Mon May 25 20:23:02 2020 +0800 +++ b/dropbearkey.1 Tue May 26 00:24:02 2020 +0800 @@ -13,7 +13,7 @@ .SH DESCRIPTION .B dropbearkey generates a -\fIRSA\fR, \fIDSS\fR, or \fIECDSA\fR +\fIRSA\fR, \fIDSS\fR, \fIECDSA\fR, or \fIEd25519\fR format SSH private key, and saves it to a file for the use with the Dropbear client or server. Note that @@ -26,6 +26,7 @@ Must be one of .I rsa .I ecdsa +.I ed25519 or .IR dss . .TP diff -r a53e7d2d60be -r 41bf8f216644 gcm.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gcm.c Tue May 26 00:24:02 2020 +0800 @@ -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 -r a53e7d2d60be -r 41bf8f216644 gcm.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gcm.h Tue May 26 00:24:02 2020 +0800 @@ -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 -r a53e7d2d60be -r 41bf8f216644 libtomcrypt/src/headers/tomcrypt_dropbear.h --- a/libtomcrypt/src/headers/tomcrypt_dropbear.h Mon May 25 20:23:02 2020 +0800 +++ b/libtomcrypt/src/headers/tomcrypt_dropbear.h Tue May 26 00:24:02 2020 +0800 @@ -27,7 +27,7 @@ #define LTC_DES #endif -#if DROPBEAR_ENABLE_CTR_MODE +#if DROPBEAR_ENABLE_CBC_MODE #define LTC_CBC_MODE #endif @@ -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 -r a53e7d2d60be -r 41bf8f216644 packet.c --- a/packet.c Mon May 25 20:23:02 2020 +0800 +++ b/packet.c Tue May 26 00:24:02 2020 +0800 @@ -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; diff -r a53e7d2d60be -r 41bf8f216644 process-packet.c --- a/process-packet.c Mon May 25 20:23:02 2020 +0800 +++ b/process-packet.c Tue May 26 00:24:02 2020 +0800 @@ -76,7 +76,9 @@ idle detection. This is slightly incorrect since a tcp forwarded global request with failure won't trigger the idle timeout, but that's probably acceptable */ - if (!(type == SSH_MSG_GLOBAL_REQUEST || type == SSH_MSG_REQUEST_FAILURE)) { + if (!(type == SSH_MSG_GLOBAL_REQUEST + || type == SSH_MSG_REQUEST_FAILURE + || type == SSH_MSG_CHANNEL_FAILURE)) { ses.last_packet_time_idle = now; } diff -r a53e7d2d60be -r 41bf8f216644 scp.c --- a/scp.c Mon May 25 20:23:02 2020 +0800 +++ b/scp.c Tue May 26 00:24:02 2020 +0800 @@ -935,7 +935,8 @@ size = size * 10 + (*cp++ - '0'); if (*cp++ != ' ') SCREWUP("size not delimited"); - if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) { + if (*cp == '\0' || strchr(cp, '/') != NULL || + strcmp(cp, ".") == 0 || strcmp(cp, "..") == 0) { run_err("error: unexpected filename: %s", cp); exit(1); } diff -r a53e7d2d60be -r 41bf8f216644 session.h --- a/session.h Mon May 25 20:23:02 2020 +0800 +++ b/session.h Tue May 26 00:24:02 2020 +0800 @@ -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; @@ -77,10 +79,18 @@ #endif /* actual keys */ union { +#if DROPBEAR_ENABLE_CBC_MODE symmetric_CBC cbc; +#endif #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 -r a53e7d2d60be -r 41bf8f216644 sysoptions.h --- a/sysoptions.h Mon May 25 20:23:02 2020 +0800 +++ b/sysoptions.h Tue May 26 00:24:02 2020 +0800 @@ -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 @@ -219,6 +223,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)) @@ -261,7 +267,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