[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 2/4] chachapoly: handle packet encryption with authenticated encryption mode
[Thread Prev] | [Thread Next]
- Subject: [PATCH 2/4] chachapoly: handle packet encryption with authenticated encryption mode
- From: Meng Hourk Tan <mtan@xxxxxxxxxx>
- Reply-to: libssh@xxxxxxxxxx
- Date: Mon, 18 Sep 2017 09:49:19 +0000
- To: "libssh@xxxxxxxxxx" <libssh@xxxxxxxxxx>
From a991c4af004f7f761a702f3f4a4cb9747068c1f2 Mon Sep 17 00:00:00 2001
From: Meng Tan <mtan@xxxxxxxxxx>
Date: Mon, 18 Sep 2017 11:34:50 +0200
Subject: [PATCH 2/4] chachapoly: handle packet encryption with authenticated
encryption mode
Signed-off-by: Meng Tan <mtan@xxxxxxxxxx>
---
src/packet.c | 81 +++++++++++++++++++++++++++++++++++-------------------
src/packet_crypt.c | 37 +++++++++++++++++++------
2 files changed, 82 insertions(+), 36 deletions(-)
diff --git a/src/packet.c b/src/packet.c
index 6e84dc8..592d7d7 100644
--- a/src/packet.c
+++ b/src/packet.c
@@ -110,7 +110,7 @@ static ssh_packet_callback default_packet_handlers[]= {
ssh_packet_global_request, // SSH2_MSG_GLOBAL_REQUEST 80
#else /* WITH_SERVER */
NULL,
-#endif /* WITH_SERVER */
+#endif /* WITH_SERVER */
ssh_request_success, // SSH2_MSG_REQUEST_SUCCESS 81
ssh_request_denied, // SSH2_MSG_REQUEST_FAILURE 82
NULL, NULL, NULL, NULL, NULL, NULL, NULL,// 83-89
@@ -144,6 +144,11 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
ssh_session session= (ssh_session) user;
unsigned int blocksize = (session->current_crypto ?
session->current_crypto->in_cipher->blocksize : 8);
+ unsigned int authlen = (session->current_crypto ?
+ session->current_crypto->in_cipher->authlen : 0);
+ unsigned int aadlen = authlen ? 4 : 0;
+ /* needlen is the number of bytes needed to read length */
+ unsigned int needlen = aadlen ? aadlen : blocksize;
unsigned char mac[DIGEST_MAX_LEN] = {0};
char buffer[16] = {0};
size_t current_macsize = 0;
@@ -155,7 +160,8 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
size_t processed = 0; /* number of byte processed from the callback */
if(session->current_crypto != NULL) {
- current_macsize = hmac_digest_len(session->current_crypto->in_hmac);
+ current_macsize = (authlen ? 0 :
+ hmac_digest_len(session->current_crypto->in_hmac));
}
if (data == NULL) {
@@ -168,7 +174,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
switch(session->packet_state) {
case PACKET_STATE_INIT:
- if (receivedlen < blocksize) {
+ if (receivedlen < needlen) {
/*
* We didn't receive enough data to read at least one
* block size, give up
@@ -190,11 +196,12 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
}
}
- memcpy(buffer, data, blocksize);
- processed += blocksize;
+ memcpy(buffer, data, needlen);
+ processed += needlen;
len = ssh_packet_decrypt_len(session, buffer);
- rc = ssh_buffer_add_data(session->in_buffer, buffer, blocksize);
+ /* buffer (packet_len) is still encrypted for chachapoly */
+ rc = ssh_buffer_add_data(session->in_buffer, buffer, needlen);
if (rc < 0) {
goto error;
}
@@ -207,7 +214,7 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
goto error;
}
- to_be_read = len - blocksize + sizeof(uint32_t);
+ to_be_read = len - needlen + sizeof(uint32_t);
if (to_be_read < 0) {
/* remote sshd sends invalid sizes? */
ssh_set_error(session,
@@ -223,8 +230,8 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
FALL_THROUGH;
case PACKET_STATE_SIZEREAD:
len = session->in_packet.len;
- to_be_read = len - blocksize + sizeof(uint32_t) + current_macsize;
- /* if to_be_read is zero, the whole packet was blocksize bytes. */
+ to_be_read = len - needlen + sizeof(uint32_t) + current_macsize + authlen;
+ /* if to_be_read is zero, the whole packet was needlen bytes. */
if (to_be_read != 0) {
if (receivedlen - processed < (unsigned int)to_be_read) {
/* give up, not enough data in buffer */
@@ -238,10 +245,10 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
packet,
to_be_read - current_macsize);
#endif
-
+ /* add mac for Authenticated Encryption Mode (AEM) */
rc = ssh_buffer_add_data(session->in_buffer,
- packet,
- to_be_read - current_macsize);
+ packet,
+ to_be_read - current_macsize);
if (rc < 0) {
goto error;
}
@@ -254,11 +261,12 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
* have been decrypted)
*/
uint32_t buffer_len = ssh_buffer_get_len(session->in_buffer);
+ uint32_t offset = aadlen ? 0 : blocksize;
/* The following check avoids decrypting zero bytes */
- if (buffer_len > blocksize) {
- uint8_t *payload = ((uint8_t*)ssh_buffer_get(session->in_buffer) + blocksize);
- uint32_t plen = buffer_len - blocksize;
+ if (buffer_len > offset) {
+ uint8_t *payload = ((uint8_t*)ssh_buffer_get(session->in_buffer) + offset);
+ uint32_t plen = buffer_len - offset - authlen;
rc = ssh_packet_decrypt(session, payload, plen);
if (rc < 0) {
@@ -266,17 +274,20 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
goto error;
}
}
-
- /* copy the last part from the incoming buffer */
- packet = ((uint8_t *)data) + processed;
- memcpy(mac, packet, current_macsize);
-
- rc = ssh_packet_hmac_verify(session, session->in_buffer, mac, session->current_crypto->in_hmac);
- if (rc < 0) {
- ssh_set_error(session, SSH_FATAL, "HMAC error");
- goto error;
+ /* mac already verified in decryption for AEM */
+ if (authlen == 0) {
+ /* copy the last part from the incoming buffer */
+ packet = ((uint8_t *)data) + processed;
+ memcpy(mac, packet, current_macsize);
+
+ rc = ssh_packet_hmac_verify(session, session->in_buffer, mac,
+ session->current_crypto->in_hmac);
+ if (rc < 0) {
+ ssh_set_error(session, SSH_FATAL, "HMAC error");
+ goto error;
+ }
+ processed += current_macsize;
}
- processed += current_macsize;
}
/* skip the size field which has been processed before */
@@ -290,6 +301,11 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
goto error;
}
+ if (authlen != 0) {
+ /* skip the mac at the end for AEM */
+ ssh_buffer_pass_bytes_end(session->in_buffer, authlen);
+ }
+
if (padding > ssh_buffer_get_len(session->in_buffer)) {
ssh_set_error(session,
SSH_FATAL,
@@ -545,8 +561,12 @@ static int packet_send2(ssh_session session) {
unsigned char *hmac = NULL;
char padstring[32] = { 0 };
int rc = SSH_ERROR;
- uint32_t finallen,payloadsize,compsize;
+ uint32_t finallen,payloadsize,compsize,headsize;
uint8_t padding;
+ unsigned int authlen = (session->current_crypto ?
+ session->current_crypto->out_cipher->authlen : 0);
+ unsigned int aadlen = authlen ? 4 : 0;
+ unsigned int hmac_len = (authlen ? authlen : hmac_digest_len(hmac_type));
uint8_t header[sizeof(padding) + sizeof(finallen)] = { 0 };
@@ -562,7 +582,12 @@ static int packet_send2(ssh_session session) {
}
#endif /* WITH_ZLIB */
compsize = currentlen;
- padding = (blocksize - ((currentlen +5) % blocksize));
+
+ /* size of packet_len (4 bytes) and padding_len (1 byte) */
+ /* packet_len not encrypted for EtM or AEM */
+ headsize = 5 - aadlen;
+
+ padding = (blocksize - ((currentlen + headsize) % blocksize));
if(padding < 4) {
padding += blocksize;
}
@@ -593,7 +618,7 @@ static int packet_send2(ssh_session session) {
hmac = ssh_packet_encrypt(session, ssh_buffer_get(session->out_buffer),
ssh_buffer_get_len(session->out_buffer));
if (hmac) {
- rc = ssh_buffer_add_data(session->out_buffer, hmac, hmac_digest_len(hmac_type));
+ rc = ssh_buffer_add_data(session->out_buffer, hmac, hmac_len);
if (rc < 0) {
goto error;
}
diff --git a/src/packet_crypt.c b/src/packet_crypt.c
index 1ac9185..6265844 100644
--- a/src/packet_crypt.c
+++ b/src/packet_crypt.c
@@ -57,14 +57,23 @@ uint32_t ssh_packet_decrypt_len(ssh_session session, char *crypted){
return ntohl(decrypted);
}
+/*
+ * 'data' contains:
+ * - aadlen packet len part (EtM or AEM)
+ * - payload to decrypt ((len - aadlen) size)
+ * - authlen mac for AEM
+ */
int ssh_packet_decrypt(ssh_session session, void *data,uint32_t len) {
struct ssh_cipher_struct *crypto = session->current_crypto->in_cipher;
char *out = NULL;
+ /* TODO: aadlen and authlen should be passed in parameters */
+ unsigned int aadlen;
int res = 0;
assert(len);
+ aadlen = crypto->authlen ? 4 : 0;
- if(len % session->current_crypto->in_cipher->blocksize != 0){
+ if(len % crypto->blocksize != aadlen){
ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be set on at least one blocksize (received %d)",len);
return SSH_ERROR;
}
@@ -89,11 +98,17 @@ int ssh_packet_decrypt(ssh_session session, void *data,uint32_t len) {
return 0;
}
+/*
+ * 'data' can contains:
+ * - packet_len part (for EtM or AEM of aadlen size)
+ * - payload to encrypt ((len - aadlen) size)
+ */
unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len) {
struct ssh_cipher_struct *crypto = NULL;
HMACCTX ctx = NULL;
- char *out = NULL;
- unsigned int finallen;
+ unsigned char *out = NULL;
+ /* TODO: aadlen and authlen should be passed in parameters */
+ unsigned int finallen, aadlen, authlen;
uint32_t seq;
enum ssh_hmac_e type;
int res = 0;
@@ -103,20 +118,23 @@ 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){
+ crypto = session->current_crypto->out_cipher;
+ authlen = crypto->authlen;
+ aadlen = authlen ? 4 : 0;
+
+ if(len % crypto->blocksize != aadlen){
ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be set on at least one blocksize (received %d)",len);
return NULL;
}
- out = malloc(len);
+ out = malloc(len + authlen);
if (out == NULL) {
return NULL;
}
type = session->current_crypto->out_hmac;
seq = ntohl(session->send_seq);
- crypto = session->current_crypto->out_cipher;
- if (session->version == 2) {
+ if (session->version == 2 && authlen == 0) {
ctx = hmac_init(session->current_crypto->encryptMAC, hmac_digest_len(type), type);
if (ctx == NULL) {
SAFE_FREE(out);
@@ -142,7 +160,10 @@ unsigned char *ssh_packet_encrypt(ssh_session session, void *data, uint32_t len)
}
memcpy(data, out, len);
- BURN_BUFFER(out, len);
+ if (session->version == 2 && authlen) {
+ memcpy(session->current_crypto->hmacbuf, out + len, authlen);
+ }
+ BURN_BUFFER(out, len + authlen);
SAFE_FREE(out);
if (session->version == 2) {
--
2.1.4
Archive administrator: postmaster@lists.cynapses.org