aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAris Adamantiadis <aris@0xbadc0de.be>2015-02-01 22:16:19 +0100
committerAris Adamantiadis <aris@0xbadc0de.be>2015-06-05 18:51:38 +0200
commitbda8ecfc1fde686170fbc6633fbfa07555a85b4c (patch)
tree1e4dfcc362176d07c78cf27bd2b06f7ca8dcd6fc
parent867b66ce0f70b96c4646a5af447efb4f2643a515 (diff)
downloadlibssh-bda8ecfc1fde686170fbc6633fbfa07555a85b4c.tar.gz
libssh-bda8ecfc1fde686170fbc6633fbfa07555a85b4c.tar.xz
libssh-bda8ecfc1fde686170fbc6633fbfa07555a85b4c.zip
chacha: packet encryption
-rw-r--r--include/libssh/crypto.h5
-rw-r--r--include/libssh/libcrypto.h1
-rw-r--r--include/libssh/wrapper.h3
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/chachapoly.c106
-rw-r--r--src/dh.c3
-rw-r--r--src/kex.c4
-rw-r--r--src/libcrypto.c13
-rw-r--r--src/packet.c40
-rw-r--r--src/packet_crypt.c39
-rw-r--r--src/wrapper.c62
11 files changed, 221 insertions, 56 deletions
diff --git a/include/libssh/crypto.h b/include/libssh/crypto.h
index 61a2b27b..9b2aa618 100644
--- a/include/libssh/crypto.h
+++ b/include/libssh/crypto.h
@@ -104,6 +104,7 @@ struct ssh_crypto_struct {
struct ssh_cipher_struct {
const char *name; /* ssh name of the algorithm */
unsigned int blocksize; /* blocksize of the algo */
+ unsigned int lenfield_blocksize; /* blocksize of the packet length field */
unsigned int keylen; /* length of the key structure */
#ifdef HAVE_LIBGCRYPT
gcry_cipher_hd_t *key;
@@ -111,7 +112,9 @@ struct ssh_cipher_struct {
void *key; /* a key buffer allocated for the algo */
void *IV;
#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);
@@ -119,6 +122,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);
};
/* vim: set ts=2 sw=2 et cindent: */
diff --git a/include/libssh/libcrypto.h b/include/libssh/libcrypto.h
index c3783880..c8950541 100644
--- a/include/libssh/libcrypto.h
+++ b/include/libssh/libcrypto.h
@@ -97,6 +97,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 cdd72d6d..72448f78 100644
--- a/include/libssh/wrapper.h
+++ b/include/libssh/wrapper.h
@@ -38,7 +38,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 621f7221..9130c257 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -111,6 +111,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@openssh.com",
+ .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 e489a1d5..f3ebcff3 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[] = {
@@ -199,7 +200,7 @@ int ssh_crypto_init(void) {
bignum_bin2bn(p_group14_value, P_GROUP14_LEN, p_group14);
OpenSSL_add_all_algorithms();
-
+ libcrypto_init();
#endif
ssh_crypto_initialized = 1;
diff --git a/src/kex.c b/src/kex.c
index f05008b0..07e36c96 100644
--- a/src/kex.c
+++ b/src/kex.c
@@ -81,6 +81,8 @@
#define ECDH ""
#endif
+#define CHACHA20 "chacha20-poly1305@openssh.com,"
+
#define KEY_EXCHANGE CURVE25519 ECDH "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
#define KEX_METHODS_SIZE 10
@@ -103,7 +105,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-sha1,hmac-sha2-256,hmac-sha2-512",
"hmac-sha1,hmac-sha2-256,hmac-sha2-512",
diff --git a/src/libcrypto.c b/src/libcrypto.c
index 600b27e2..5d232854 100644
--- a/src/libcrypto.c
+++ b/src/libcrypto.c
@@ -63,6 +63,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 {
@@ -668,10 +669,22 @@ static struct ssh_cipher_struct ssh_ciphertab[] = {
},
#endif /* HAS_DES */
{
+ .name = "chacha20-poly1305@openssh.com"
+ },
+ {
.name = NULL,
}
};
+void libcrypto_init(void){
+ int i;
+ for (i=0; ssh_ciphertab[i].name != NULL; ++i){
+ if(strcmp(ssh_ciphertab[i].name, "chacha20-poly1305@openssh.com") == 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 3c927dc8..f90cfd27 100644
--- a/src/packet.c
+++ b/src/packet.c
@@ -524,16 +524,17 @@ 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 = buffer_get_rest_len(session->out_buffer);
unsigned char *hmac = NULL;
char padstring[32] = { 0 };
int rc = SSH_ERROR;
- uint32_t finallen,payloadsize,compsize;
+ 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
@@ -547,20 +548,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 = buffer_prepend_data(session->out_buffer, &header, sizeof(header));
+ rc = buffer_prepend_data(session->out_buffer, buffer_get_rest(header_buffer),
+ buffer_get_rest_len(header_buffer));
if (rc < 0) {
goto error;
}
@@ -570,9 +580,9 @@ 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,
- buffer_get_rest(session->out_buffer),buffer_get_rest_len(session->out_buffer)
- ,buffer_get_rest_len(session->out_buffer));
+ ssh_pcap_context_write(session->pcap_ctx,SSH_PCAP_DIR_OUT,
+ buffer_get_rest(session->out_buffer),buffer_get_rest_len(session->out_buffer)
+ ,buffer_get_rest_len(session->out_buffer));
}
#endif
hmac = packet_encrypt(session, buffer_get_rest(session->out_buffer),
@@ -593,12 +603,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 914727e0..03a94d94 100644
--- a/src/packet_crypt.c
+++ b/src/packet_crypt.c
@@ -98,7 +98,7 @@ unsigned char *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;
}
@@ -117,26 +117,29 @@ unsigned char *packet_encrypt(ssh_session session, void *data, uint32_t len) {
return NULL;
}
- 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);
#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);
BURN_BUFFER(out, len);
SAFE_FREE(out);
diff --git a/src/wrapper.c b/src/wrapper.c
index c1dd4d03..ba53ad09 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;
}
@@ -123,6 +127,9 @@ void ssh_cipher_clear(struct ssh_cipher_struct *cipher){
#endif
SAFE_FREE(cipher->key);
}
+ if (cipher->chacha20_schedule != NULL){
+ SAFE_FREE(cipher->chacha20_schedule);
+ }
}
static void cipher_free(struct ssh_cipher_struct *cipher) {
@@ -238,9 +245,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++;
}
@@ -348,7 +360,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();
@@ -378,7 +390,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++;
@@ -396,23 +433,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++;