[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 06/20] chacha: packet encryption
[Thread Prev] | [Thread Next]
- Subject: [PATCH 06/20] chacha: packet encryption
- From: Alberto Aguirre <albaguirre@xxxxxxxxx>
- Reply-to: libssh@xxxxxxxxxx
- Date: Wed, 28 Feb 2018 10:24:53 -0600
- To: libssh@xxxxxxxxxx
From: Aris Adamantiadis <aris@xxxxxxxxxxxx> --- include/libssh/crypto.h | 6 +++ include/libssh/libcrypto.h | 1 + include/libssh/wrapper.h | 3 +- src/CMakeLists.txt | 1 + src/chachapoly.c | 106 +++++++++++++++++++++++++++++++++++++++++++++ src/dh.c | 3 ++ src/kex.c | 4 +- src/libcrypto.c | 12 ++++- src/packet.c | 36 ++++++++++----- src/packet_crypt.c | 46 ++++++++++++-------- src/wrapper.c | 62 +++++++++++++++++--------- 11 files changed, 226 insertions(+), 54 deletions(-) create mode 100644 src/chachapoly.c diff --git a/include/libssh/crypto.h b/include/libssh/crypto.h index 81915e20..83391b69 100644 --- a/include/libssh/crypto.h +++ b/include/libssh/crypto.h @@ -129,10 +129,12 @@ struct ssh_cipher_struct { const char *name; /* ssh name of the algorithm */ unsigned int blocksize; /* blocksize of the algo */ enum ssh_cipher_e ciphertype; + unsigned int lenfield_blocksize; /* blocksize of the packet length field */ #ifdef HAVE_LIBGCRYPT size_t keylen; /* length of the key structure */ gcry_cipher_hd_t *key; #elif defined HAVE_LIBCRYPTO + size_t keylen; /* length of the key structure */ struct ssh_3des_key_schedule *des3_key; struct ssh_aes_key_schedule *aes_key; const EVP_CIPHER *cipher; @@ -142,7 +144,9 @@ struct ssh_cipher_struct { mbedtls_cipher_context_t decrypt_ctx; mbedtls_cipher_type_t type; #endif + struct chacha20_poly1305_keysched *chacha20_schedule; unsigned int keysize; /* bytes of key used. != keylen */ + size_t tag_size; /* overhead required for tag */ /* sets the new key for immediate use */ int (*set_encrypt_key)(struct ssh_cipher_struct *cipher, void *key, void *IV); int (*set_decrypt_key)(struct ssh_cipher_struct *cipher, void *key, void *IV); @@ -150,6 +154,8 @@ struct ssh_cipher_struct { unsigned long len); void (*decrypt)(struct ssh_cipher_struct *cipher, void *in, void *out, unsigned long len); + void (*aead_encrypt)(struct ssh_cipher_struct *cipher, void *in, void *out, + size_t len, uint8_t *mac, uint64_t seq); void (*cleanup)(struct ssh_cipher_struct *cipher); }; diff --git a/include/libssh/libcrypto.h b/include/libssh/libcrypto.h index 6a08837a..4b8e5414 100644 --- a/include/libssh/libcrypto.h +++ b/include/libssh/libcrypto.h @@ -95,6 +95,7 @@ SHA512CTX sha512_init(void); void sha512_update(SHA512CTX c, const void *data, unsigned long len); void sha512_final(unsigned char *md, SHA512CTX c); +void libcrypto_init(void); struct ssh_cipher_struct *ssh_get_ciphertab(void); #endif /* HAVE_LIBCRYPTO */ diff --git a/include/libssh/wrapper.h b/include/libssh/wrapper.h index 6b6cf0b1..c23c9061 100644 --- a/include/libssh/wrapper.h +++ b/include/libssh/wrapper.h @@ -39,7 +39,8 @@ enum ssh_hmac_e { SSH_HMAC_SHA256, SSH_HMAC_SHA384, SSH_HMAC_SHA512, - SSH_HMAC_MD5 + SSH_HMAC_MD5, + SSH_HMAC_AEAD_POLY1305 }; enum ssh_des_e { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index eaa2f067..8f7fdf40 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -122,6 +122,7 @@ set(libssh_SRCS bignum.c buffer.c callbacks.c + chachapoly.c channels.c client.c config.c diff --git a/src/chachapoly.c b/src/chachapoly.c new file mode 100644 index 00000000..eeb12da8 --- /dev/null +++ b/src/chachapoly.c @@ -0,0 +1,106 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2015 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" +#include "libssh/libssh.h" +#include "libssh/crypto.h" +#include "libssh/chacha.h" +#include "libssh/poly1305.h" +#include "libssh/misc.h" + +/* size of the keys k1 and k2 as defined in specs */ +#define CHACHA20_KEYLEN 32 +struct chacha20_poly1305_keysched { + /* key used for encrypting the length field*/ + struct chacha_ctx k1; + /* key used for encrypting the packets */ + struct chacha_ctx k2; +}; + +struct ssh_packet_header { + uint32_t length; + uint8_t payload[]; +} __attribute__((packed)); + +const uint8_t zero_block_counter[8] = {0, 0, 0, 0, 0, 0, 0, 0}; +const uint8_t payload_block_counter[8] = {1, 0, 0, 0, 0, 0, 0, 0}; + +static int chacha20_set_encrypt_key(struct ssh_cipher_struct *cipher, + void *key, void *IV){ + struct chacha20_poly1305_keysched *sched; + uint8_t *key2 = key; + (void)IV; + + if(cipher->chacha20_schedule == NULL){ + sched = malloc(sizeof *sched); + if (sched == NULL){ + return -1; + } + } else { + sched = cipher->chacha20_schedule; + } + chacha_keysetup(&sched->k2, key2, CHACHA20_KEYLEN * 8); + chacha_keysetup(&sched->k1, key2 + CHACHA20_KEYLEN, CHACHA20_KEYLEN * 8); + cipher->chacha20_schedule = sched; + return 0; +} + +/** @internal + * @brief encrypts an outgoing packet with chacha20 and authenticate it + * with poly1305. + */ +static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher, + void *in, void *out, size_t len, uint8_t *tag, uint64_t seq){ + struct ssh_packet_header *in_packet = in, *out_packet = out; + uint8_t poly1305_ctx[POLY1305_KEYLEN]; + struct chacha20_poly1305_keysched *keys = cipher->chacha20_schedule; + + seq = htonll(seq); + /* step 1, prepare the poly1305 key */ + ZERO_STRUCT(poly1305_ctx); + chacha_ivsetup(&keys->k2, (uint8_t *)&seq, zero_block_counter); + chacha_encrypt_bytes(&keys->k2, poly1305_ctx, poly1305_ctx, POLY1305_KEYLEN); + + /* step 2, encrypt length field */ + chacha_ivsetup(&keys->k1, (uint8_t *)&seq, zero_block_counter); + chacha_encrypt_bytes(&keys->k1,(uint8_t *)&in_packet->length, + (uint8_t *)&out_packet->length, sizeof(uint32_t)); + + /* step 3, encrypt packet payload */ + chacha_ivsetup(&keys->k2, (uint8_t *)&seq, payload_block_counter); + chacha_encrypt_bytes(&keys->k2, in_packet->payload, out_packet->payload, + len - sizeof(uint32_t)); + + /* step 4, compute the MAC */ + poly1305_auth(tag, (uint8_t *)out_packet, len, poly1305_ctx); +} + +const struct ssh_cipher_struct chacha20poly1305_cipher = { + .name = "chacha20-poly1305@xxxxxxxxxxx", + .blocksize = 8, + .lenfield_blocksize = 4, + .keylen = sizeof(struct chacha20_poly1305_keysched), + .keysize = 512, + .tag_size = POLY1305_TAGLEN, + .set_encrypt_key = chacha20_set_encrypt_key, + .set_decrypt_key = chacha20_set_encrypt_key, + .aead_encrypt = chacha20_poly1305_aead_encrypt, +}; diff --git a/src/dh.c b/src/dh.c index 26e47994..f29627c5 100644 --- a/src/dh.c +++ b/src/dh.c @@ -68,6 +68,7 @@ #include <openssl/rand.h> #include <openssl/evp.h> #include <openssl/err.h> +#include "libssh/libcrypto.h" #endif static unsigned char p_group1_value[] = { @@ -210,6 +211,8 @@ int ssh_crypto_init(void) { bignum_bin2bn(p_group14_value, P_GROUP14_LEN, p_group14); OpenSSL_add_all_algorithms(); + + libcrypto_init(); #elif defined HAVE_LIBMBEDCRYPTO p_group1 = bignum_new(); bignum_bin2bn(p_group1_value, P_GROUP1_LEN, p_group1); diff --git a/src/kex.c b/src/kex.c index b658ed44..20044c32 100644 --- a/src/kex.c +++ b/src/kex.c @@ -95,6 +95,8 @@ #define ECDH "" #endif +#define CHACHA20 "chacha20-poly1305@xxxxxxxxxxx," + #define KEY_EXCHANGE CURVE25519 ECDH "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1" #define KEX_METHODS_SIZE 10 @@ -117,7 +119,7 @@ static const char *default_methods[] = { static const char *supported_methods[] = { KEY_EXCHANGE, HOSTKEYS, - AES BLOWFISH DES_SUPPORTED, + CHACHA20 AES BLOWFISH DES_SUPPORTED, AES BLOWFISH DES_SUPPORTED, "hmac-sha2-256,hmac-sha2-512,hmac-sha1", "hmac-sha2-256,hmac-sha2-512,hmac-sha1", diff --git a/src/libcrypto.c b/src/libcrypto.c index 66453666..0ed7b7bb 100644 --- a/src/libcrypto.c +++ b/src/libcrypto.c @@ -60,6 +60,7 @@ #include "libssh/crypto.h" +extern const struct ssh_cipher_struct chacha20poly1305_cipher; struct ssh_mac_ctx_struct { enum ssh_mac_e mac_type; union { @@ -861,10 +862,19 @@ static struct ssh_cipher_struct ssh_ciphertab[] = { }, #endif /* HAS_DES */ { - .name = NULL + .name = "chacha20-poly1305@xxxxxxxxxxx" } }; +void libcrypto_init(void){ + int i; + for (i=0; ssh_ciphertab[i].name != NULL; ++i){ + if(strcmp(ssh_ciphertab[i].name, "chacha20-poly1305@xxxxxxxxxxx") == 0){ + memcpy(&ssh_ciphertab[i], &chacha20poly1305_cipher, sizeof(struct ssh_cipher_struct)); + break; + } + } +} struct ssh_cipher_struct *ssh_get_ciphertab(void) { diff --git a/src/packet.c b/src/packet.c index 57eacc4d..e989fa28 100644 --- a/src/packet.c +++ b/src/packet.c @@ -547,6 +547,8 @@ static int ssh_packet_write(ssh_session session) { static int packet_send2(ssh_session session) { unsigned int blocksize = (session->current_crypto ? session->current_crypto->out_cipher->blocksize : 8); + unsigned int lenfield_blocksize = (session->current_crypto ? + session->current_crypto->out_cipher->lenfield_blocksize : 0); enum ssh_hmac_e hmac_type = (session->current_crypto ? session->current_crypto->out_hmac : session->next_crypto->out_hmac); uint32_t currentlen = ssh_buffer_get_len(session->out_buffer); @@ -555,8 +557,7 @@ static int packet_send2(ssh_session session) { int rc = SSH_ERROR; uint32_t finallen,payloadsize,compsize; uint8_t padding; - - uint8_t header[sizeof(padding) + sizeof(finallen)] = { 0 }; + ssh_buffer header_buffer = ssh_buffer_new(); payloadsize = currentlen; #ifdef WITH_ZLIB @@ -570,20 +571,29 @@ static int packet_send2(ssh_session session) { } #endif /* WITH_ZLIB */ compsize = currentlen; - padding = (blocksize - ((currentlen +5) % blocksize)); + /* compressed payload + packet len (4) + padding len (1) */ + /* totallen - lenfield_blocksize must be equal to 0 (mod blocksize) */ + padding = (blocksize - ((blocksize - lenfield_blocksize + currentlen + 5) % blocksize)); if(padding < 4) { padding += blocksize; } - if (session->current_crypto) { + if (session->current_crypto != NULL) { ssh_get_random(padstring, padding, 0); } - finallen = htonl(currentlen + padding + 1); + if (header_buffer == NULL){ + ssh_set_error_oom(session); + goto error; + } + finallen = currentlen + padding + 1; + rc = ssh_buffer_pack(header_buffer, "db", finallen, padding); + if (rc == SSH_ERROR){ + goto error; + } - memcpy(&header[0], &finallen, sizeof(finallen)); - header[sizeof(finallen)] = padding; - rc = ssh_buffer_prepend_data(session->out_buffer, &header, sizeof(header)); + rc = ssh_buffer_prepend_data(session->out_buffer, ssh_buffer_get(header_buffer), + ssh_buffer_get_len(header_buffer)); if (rc < 0) { goto error; } @@ -594,8 +604,8 @@ static int packet_send2(ssh_session session) { #ifdef WITH_PCAP if(session->pcap_ctx){ ssh_pcap_context_write(session->pcap_ctx,SSH_PCAP_DIR_OUT, - ssh_buffer_get(session->out_buffer),ssh_buffer_get_len(session->out_buffer) - ,ssh_buffer_get_len(session->out_buffer)); + ssh_buffer_get(session->out_buffer),ssh_buffer_get_len(session->out_buffer), + ssh_buffer_get_len(session->out_buffer)); } #endif hmac = ssh_packet_encrypt(session, ssh_buffer_get(session->out_buffer), @@ -616,12 +626,14 @@ static int packet_send2(ssh_session session) { SSH_LOG(SSH_LOG_PACKET, "packet: wrote [len=%d,padding=%hhd,comp=%d,payload=%d]", - ntohl(finallen), padding, compsize, payloadsize); + finallen, padding, compsize, payloadsize); if (ssh_buffer_reinit(session->out_buffer) < 0) { rc = SSH_ERROR; } error: - + if(header_buffer != NULL){ + ssh_buffer_free(header_buffer); + } return rc; /* SSH_OK, AGAIN or ERROR */ } diff --git a/src/packet_crypt.c b/src/packet_crypt.c index 7a30e661..5bcb6d65 100644 --- a/src/packet_crypt.c +++ b/src/packet_crypt.c @@ -93,7 +93,7 @@ unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len) if (!session->current_crypto) { return NULL; /* nothing to do here */ } - if(len % session->current_crypto->in_cipher->blocksize != 0){ + if((len - session->current_crypto->out_cipher->lenfield_blocksize) % session->current_crypto->out_cipher->blocksize != 0){ ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be set on at least one blocksize (received %d)",len); return NULL; } @@ -106,26 +106,36 @@ unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len) seq = ntohl(session->send_seq); crypto = session->current_crypto->out_cipher; - if (session->version == 2) { - ctx = hmac_init(session->current_crypto->encryptMAC, hmac_digest_len(type), type); - if (ctx == NULL) { - SAFE_FREE(out); - return NULL; - } - hmac_update(ctx,(unsigned char *)&seq,sizeof(uint32_t)); - hmac_update(ctx,data,len); - hmac_final(ctx,session->current_crypto->hmacbuf,&finallen); + if (crypto->aead_encrypt != NULL) { + crypto->aead_encrypt(crypto, data, out, len, + session->current_crypto->hmacbuf, session->send_seq); + } else { + if (session->version == 2) { + ctx = hmac_init(session->current_crypto->encryptMAC, hmac_digest_len(type), type); + if (ctx == NULL) { + SAFE_FREE(out); + return NULL; + } + hmac_update(ctx,(unsigned char *)&seq,sizeof(uint32_t)); + hmac_update(ctx,data,len); + hmac_final(ctx,session->current_crypto->hmacbuf,&finallen); + + if (crypto->set_encrypt_key(crypto, session->current_crypto->encryptkey, + session->current_crypto->encryptIV) < 0) { + SAFE_FREE(out); + return NULL; + } + #ifdef DEBUG_CRYPTO - ssh_print_hexa("mac: ",data,hmac_digest_len(type)); - if (finallen != hmac_digest_len(type)) { - printf("Final len is %d\n",finallen); - } - ssh_print_hexa("Packet hmac", session->current_crypto->hmacbuf, hmac_digest_len(type)); + ssh_print_hexa("mac: ",data,hmac_digest_len(type)); + if (finallen != hmac_digest_len(type)) { + printf("Final len is %d\n",finallen); + } + ssh_print_hexa("Packet hmac", session->current_crypto->hmacbuf, hmac_digest_len(type)); #endif - } - + } crypto->encrypt(crypto, data, out, len); - + } memcpy(data, out, len); explicit_bzero(out, len); SAFE_FREE(out); diff --git a/src/wrapper.c b/src/wrapper.c index 7ecb3101..71d64bf3 100644 --- a/src/wrapper.c +++ b/src/wrapper.c @@ -47,6 +47,7 @@ #include "libssh/crypto.h" #include "libssh/wrapper.h" #include "libssh/pki.h" +#include "libssh/poly1305.h" static struct ssh_hmac_struct ssh_hmac_tab[] = { { "hmac-sha1", SSH_HMAC_SHA1 }, @@ -54,6 +55,7 @@ static struct ssh_hmac_struct ssh_hmac_tab[] = { { "hmac-sha2-384", SSH_HMAC_SHA384 }, { "hmac-sha2-512", SSH_HMAC_SHA512 }, { "hmac-md5", SSH_HMAC_MD5 }, + { "aead-poly1305", SSH_HMAC_AEAD_POLY1305 }, { NULL, 0} }; @@ -73,6 +75,8 @@ size_t hmac_digest_len(enum ssh_hmac_e type) { return SHA512_DIGEST_LEN; case SSH_HMAC_MD5: return MD5_DIGEST_LEN; + case SSH_HMAC_AEAD_POLY1305: + return POLY1305_TAGLEN; default: return 0; } @@ -124,6 +128,9 @@ void ssh_cipher_clear(struct ssh_cipher_struct *cipher){ if (cipher->cleanup != NULL) { cipher->cleanup(cipher); } + if (cipher->chacha20_schedule != NULL){ + SAFE_FREE(cipher->chacha20_schedule); + } } static void cipher_free(struct ssh_cipher_struct *cipher) { @@ -247,9 +254,14 @@ static int crypt_set_algorithms2(ssh_session session){ } i = 0; - /* we must scan the kex entries to find hmac algorithms and set their appropriate structure */ - /* out */ - wanted = session->next_crypto->kex_methods[SSH_MAC_C_S]; + if (session->next_crypto->out_cipher->aead_encrypt != NULL){ + /* this cipher has integrated MAC */ + wanted = "aead-poly1305"; + } else { + /* we must scan the kex entries to find hmac algorithms and set their appropriate structure */ + /* out */ + wanted = session->next_crypto->kex_methods[SSH_MAC_C_S]; + } while (ssh_hmactab[i].name && strcmp(wanted, ssh_hmactab[i].name)) { i++; } @@ -357,7 +369,7 @@ int crypt_set_algorithms(ssh_session session, enum ssh_des_e des_type) { #ifdef WITH_SERVER int crypt_set_algorithms_server(ssh_session session){ - char *method = NULL; + const char *method = NULL; int i = 0; struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab(); struct ssh_hmac_struct *ssh_hmactab=ssh_get_hmactab(); @@ -387,7 +399,32 @@ int crypt_set_algorithms_server(ssh_session session){ return SSH_ERROR; } i=0; + if (session->next_crypto->out_cipher->aead_encrypt != NULL){ + /* this cipher has integrated MAC */ + method = "aead-poly1305"; + } else { + /* we must scan the kex entries to find hmac algorithms and set their appropriate structure */ + /* out */ + method = session->next_crypto->kex_methods[SSH_MAC_S_C]; + } + /* HMAC algorithm selection */ + + while (ssh_hmactab[i].name && strcmp(method, ssh_hmactab[i].name)) { + i++; + } + + if (ssh_hmactab[i].name == NULL) { + ssh_set_error(session, SSH_FATAL, + "crypt_set_algorithms_server: no hmac algorithm function found for %s", + method); + return SSH_ERROR; + } + SSH_LOG(SSH_LOG_PACKET, "Set HMAC output algorithm to %s", method); + + session->next_crypto->out_hmac = ssh_hmactab[i].hmac_type; + /* in */ + i=0; method = session->next_crypto->kex_methods[SSH_CRYPT_C_S]; while(ssh_ciphertab[i].name && strcmp(method,ssh_ciphertab[i].name)) i++; @@ -405,23 +442,6 @@ int crypt_set_algorithms_server(ssh_session session){ } i=0; - /* HMAC algorithm selection */ - method = session->next_crypto->kex_methods[SSH_MAC_S_C]; - while (ssh_hmactab[i].name && strcmp(method, ssh_hmactab[i].name)) { - i++; - } - - if (ssh_hmactab[i].name == NULL) { - ssh_set_error(session, SSH_FATAL, - "crypt_set_algorithms_server: no hmac algorithm function found for %s", - method); - return SSH_ERROR; - } - SSH_LOG(SSH_LOG_PACKET, "Set HMAC output algorithm to %s", method); - - session->next_crypto->out_hmac = ssh_hmactab[i].hmac_type; - i=0; - method = session->next_crypto->kex_methods[SSH_MAC_C_S]; while (ssh_hmactab[i].name && strcmp(method, ssh_hmactab[i].name)) { i++; -- 2.14.1
[PATCH 00/20] Add chacha20-poly1305 support | Alberto Aguirre <albaguirre@xxxxxxxxx> |