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:
Vladislav Grishenko
2020-05-25 20:50:25 +05:00
committed by GitHub
parent 91e537e427
commit d3d0d60076
13 changed files with 559 additions and 55 deletions

143
packet.c
View File

@@ -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;