mirror of
https://github.com/clearml/dropbear
synced 2025-03-09 21:41:07 +00:00
Add Chacha20-Poly1305, AES128-GCM and AES256-GCM support (#93)
* Add Chacha20-Poly1305 authenticated encryption * Add general AEAD approach. * Add chacha20-poly1305@openssh.com 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 aes128-gcm@openssh.com and aes256-gcm@openssh.com 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
This commit is contained in:
parent
91e537e427
commit
d3d0d60076
@ -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
|
||||
|
||||
|
8
algo.h
8
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 {
|
||||
|
148
chachapoly.c
Normal file
148
chachapoly.c
Normal file
@ -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 */
|
44
chachapoly.h
Normal file
44
chachapoly.h
Normal file
@ -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_ */
|
@ -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},
|
||||
|
44
common-kex.c
44
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;
|
||||
|
@ -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
|
||||
|
120
gcm.c
Normal file
120
gcm.c
Normal file
@ -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 */
|
47
gcm.h
Normal file
47
gcm.h
Normal file
@ -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_ */
|
@ -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
|
||||
|
143
packet.c
143
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);
|
||||
#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 @@ 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");
|
||||
/* 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;
|
||||
|
@ -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;
|
||||
@ -80,6 +82,12 @@ struct key_context_directional {
|
||||
symmetric_CBC cbc;
|
||||
#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];
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user