mirror of
https://github.com/libssh2/libssh2.git
synced 2025-07-29 13:01:14 +03:00
support encrypt-then-mac (etm) MACs (#987)
Support for calculating MAC (message authentication code) on encrypted data instead of plain text data. This adds support for the following MACs: - `hmac-sha1-etm@openssh.com` - `hmac-sha2-256-etm@openssh.com` - `hmac-sha2-512-etm@openssh.com` Integration-patches-by: Viktor Szakats * rebase on master * fix checksec warnings * fix compiler warning * fix indent/whitespace/eol * rebase/manual merge onto AES-GCM patch #797 * more manual merge of `libssh2_transport_send()` based on dfandrich/shellfish Fixes #582 Closes #655 Closes #987
This commit is contained in:
@ -541,7 +541,8 @@ struct transportpacket
|
|||||||
packet_length + padding_length + 4 +
|
packet_length + padding_length + 4 +
|
||||||
mac_length. */
|
mac_length. */
|
||||||
unsigned char *payload; /* this is a pointer to a LIBSSH2_ALLOC()
|
unsigned char *payload; /* this is a pointer to a LIBSSH2_ALLOC()
|
||||||
area to which we write decrypted data */
|
area to which we write incoming packet data
|
||||||
|
which is not yet decrypted in etm mode. */
|
||||||
unsigned char *wptr; /* write pointer into the payload to where we
|
unsigned char *wptr; /* write pointer into the payload to where we
|
||||||
are currently writing decrypted data */
|
are currently writing decrypted data */
|
||||||
|
|
||||||
|
49
src/mac.c
49
src/mac.c
@ -71,7 +71,8 @@ static LIBSSH2_MAC_METHOD mac_method_none = {
|
|||||||
0,
|
0,
|
||||||
NULL,
|
NULL,
|
||||||
mac_none_MAC,
|
mac_none_MAC,
|
||||||
NULL
|
NULL,
|
||||||
|
0
|
||||||
};
|
};
|
||||||
#endif /* defined(LIBSSH2DEBUG) && defined(LIBSSH2_MAC_NONE_INSECURE) */
|
#endif /* defined(LIBSSH2DEBUG) && defined(LIBSSH2_MAC_NONE_INSECURE) */
|
||||||
|
|
||||||
@ -138,8 +139,6 @@ mac_method_hmac_sha2_512_hash(LIBSSH2_SESSION * session,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static const LIBSSH2_MAC_METHOD mac_method_hmac_sha2_512 = {
|
static const LIBSSH2_MAC_METHOD mac_method_hmac_sha2_512 = {
|
||||||
"hmac-sha2-512",
|
"hmac-sha2-512",
|
||||||
64,
|
64,
|
||||||
@ -147,7 +146,19 @@ static const LIBSSH2_MAC_METHOD mac_method_hmac_sha2_512 = {
|
|||||||
mac_method_common_init,
|
mac_method_common_init,
|
||||||
mac_method_hmac_sha2_512_hash,
|
mac_method_hmac_sha2_512_hash,
|
||||||
mac_method_common_dtor,
|
mac_method_common_dtor,
|
||||||
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const LIBSSH2_MAC_METHOD mac_method_hmac_sha2_512_etm = {
|
||||||
|
"hmac-sha2-512-etm@openssh.com",
|
||||||
|
64,
|
||||||
|
64,
|
||||||
|
mac_method_common_init,
|
||||||
|
mac_method_hmac_sha2_512_hash,
|
||||||
|
mac_method_common_dtor,
|
||||||
|
1
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@ -192,7 +203,19 @@ static const LIBSSH2_MAC_METHOD mac_method_hmac_sha2_256 = {
|
|||||||
mac_method_common_init,
|
mac_method_common_init,
|
||||||
mac_method_hmac_sha2_256_hash,
|
mac_method_hmac_sha2_256_hash,
|
||||||
mac_method_common_dtor,
|
mac_method_common_dtor,
|
||||||
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const LIBSSH2_MAC_METHOD mac_method_hmac_sha2_256_etm = {
|
||||||
|
"hmac-sha2-256-etm@openssh.com",
|
||||||
|
32,
|
||||||
|
32,
|
||||||
|
mac_method_common_init,
|
||||||
|
mac_method_hmac_sha2_256_hash,
|
||||||
|
mac_method_common_dtor,
|
||||||
|
1
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@ -237,6 +260,17 @@ static const LIBSSH2_MAC_METHOD mac_method_hmac_sha1 = {
|
|||||||
mac_method_common_init,
|
mac_method_common_init,
|
||||||
mac_method_hmac_sha1_hash,
|
mac_method_hmac_sha1_hash,
|
||||||
mac_method_common_dtor,
|
mac_method_common_dtor,
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
static const LIBSSH2_MAC_METHOD mac_method_hmac_sha1_etm = {
|
||||||
|
"hmac-sha1-etm@openssh.com",
|
||||||
|
20,
|
||||||
|
20,
|
||||||
|
mac_method_common_init,
|
||||||
|
mac_method_hmac_sha1_hash,
|
||||||
|
mac_method_common_dtor,
|
||||||
|
1
|
||||||
};
|
};
|
||||||
|
|
||||||
/* mac_method_hmac_sha1_96_hash
|
/* mac_method_hmac_sha1_96_hash
|
||||||
@ -268,6 +302,7 @@ static const LIBSSH2_MAC_METHOD mac_method_hmac_sha1_96 = {
|
|||||||
mac_method_common_init,
|
mac_method_common_init,
|
||||||
mac_method_hmac_sha1_96_hash,
|
mac_method_hmac_sha1_96_hash,
|
||||||
mac_method_common_dtor,
|
mac_method_common_dtor,
|
||||||
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
#if LIBSSH2_MD5
|
#if LIBSSH2_MD5
|
||||||
@ -310,6 +345,7 @@ static const LIBSSH2_MAC_METHOD mac_method_hmac_md5 = {
|
|||||||
mac_method_common_init,
|
mac_method_common_init,
|
||||||
mac_method_hmac_md5_hash,
|
mac_method_hmac_md5_hash,
|
||||||
mac_method_common_dtor,
|
mac_method_common_dtor,
|
||||||
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
/* mac_method_hmac_md5_96_hash
|
/* mac_method_hmac_md5_96_hash
|
||||||
@ -339,6 +375,7 @@ static const LIBSSH2_MAC_METHOD mac_method_hmac_md5_96 = {
|
|||||||
mac_method_common_init,
|
mac_method_common_init,
|
||||||
mac_method_hmac_md5_96_hash,
|
mac_method_hmac_md5_96_hash,
|
||||||
mac_method_common_dtor,
|
mac_method_common_dtor,
|
||||||
|
0
|
||||||
};
|
};
|
||||||
#endif /* LIBSSH2_MD5 */
|
#endif /* LIBSSH2_MD5 */
|
||||||
|
|
||||||
@ -383,6 +420,7 @@ static const LIBSSH2_MAC_METHOD mac_method_hmac_ripemd160 = {
|
|||||||
mac_method_common_init,
|
mac_method_common_init,
|
||||||
mac_method_hmac_ripemd160_hash,
|
mac_method_hmac_ripemd160_hash,
|
||||||
mac_method_common_dtor,
|
mac_method_common_dtor,
|
||||||
|
0
|
||||||
};
|
};
|
||||||
|
|
||||||
static const LIBSSH2_MAC_METHOD mac_method_hmac_ripemd160_openssh_com = {
|
static const LIBSSH2_MAC_METHOD mac_method_hmac_ripemd160_openssh_com = {
|
||||||
@ -392,17 +430,21 @@ static const LIBSSH2_MAC_METHOD mac_method_hmac_ripemd160_openssh_com = {
|
|||||||
mac_method_common_init,
|
mac_method_common_init,
|
||||||
mac_method_hmac_ripemd160_hash,
|
mac_method_hmac_ripemd160_hash,
|
||||||
mac_method_common_dtor,
|
mac_method_common_dtor,
|
||||||
|
0
|
||||||
};
|
};
|
||||||
#endif /* LIBSSH2_HMAC_RIPEMD */
|
#endif /* LIBSSH2_HMAC_RIPEMD */
|
||||||
|
|
||||||
static const LIBSSH2_MAC_METHOD *mac_methods[] = {
|
static const LIBSSH2_MAC_METHOD *mac_methods[] = {
|
||||||
#if LIBSSH2_HMAC_SHA256
|
#if LIBSSH2_HMAC_SHA256
|
||||||
&mac_method_hmac_sha2_256,
|
&mac_method_hmac_sha2_256,
|
||||||
|
&mac_method_hmac_sha2_256_etm,
|
||||||
#endif
|
#endif
|
||||||
#if LIBSSH2_HMAC_SHA512
|
#if LIBSSH2_HMAC_SHA512
|
||||||
&mac_method_hmac_sha2_512,
|
&mac_method_hmac_sha2_512,
|
||||||
|
&mac_method_hmac_sha2_512_etm,
|
||||||
#endif
|
#endif
|
||||||
&mac_method_hmac_sha1,
|
&mac_method_hmac_sha1,
|
||||||
|
&mac_method_hmac_sha1_etm,
|
||||||
&mac_method_hmac_sha1_96,
|
&mac_method_hmac_sha1_96,
|
||||||
#if LIBSSH2_MD5
|
#if LIBSSH2_MD5
|
||||||
&mac_method_hmac_md5,
|
&mac_method_hmac_md5,
|
||||||
@ -435,6 +477,7 @@ static const LIBSSH2_MAC_METHOD mac_method_hmac_aesgcm = {
|
|||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
NULL,
|
||||||
|
0
|
||||||
};
|
};
|
||||||
#endif /* LIBSSH2_AES_GCM */
|
#endif /* LIBSSH2_AES_GCM */
|
||||||
|
|
||||||
|
@ -57,6 +57,8 @@ struct _LIBSSH2_MAC_METHOD
|
|||||||
size_t packet_len, const unsigned char *addtl,
|
size_t packet_len, const unsigned char *addtl,
|
||||||
size_t addtl_len, void **abstract);
|
size_t addtl_len, void **abstract);
|
||||||
int (*dtor) (LIBSSH2_SESSION * session, void **abstract);
|
int (*dtor) (LIBSSH2_SESSION * session, void **abstract);
|
||||||
|
|
||||||
|
int etm; /* encrypt-then-mac */
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct _LIBSSH2_MAC_METHOD LIBSSH2_MAC_METHOD;
|
typedef struct _LIBSSH2_MAC_METHOD LIBSSH2_MAC_METHOD;
|
||||||
|
231
src/transport.c
231
src/transport.c
@ -197,21 +197,81 @@ fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ )
|
|||||||
if(encrypted && !CRYPT_FLAG_L(session, INTEGRATED_MAC)) {
|
if(encrypted && !CRYPT_FLAG_L(session, INTEGRATED_MAC)) {
|
||||||
|
|
||||||
/* Calculate MAC hash */
|
/* Calculate MAC hash */
|
||||||
session->remote.mac->hash(session, macbuf, /* store hash here */
|
int etm = session->remote.mac->etm;
|
||||||
session->remote.seqno,
|
size_t mac_len = session->remote.mac->mac_len;
|
||||||
p->init, 5,
|
if(etm) {
|
||||||
p->payload,
|
/* store hash here */
|
||||||
session->fullpacket_payload_len,
|
session->remote.mac->hash(session, macbuf,
|
||||||
&session->remote.mac_abstract);
|
session->remote.seqno,
|
||||||
|
p->payload, p->total_num - mac_len,
|
||||||
|
NULL, 0,
|
||||||
|
&session->remote.mac_abstract);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* store hash here */
|
||||||
|
session->remote.mac->hash(session, macbuf,
|
||||||
|
session->remote.seqno,
|
||||||
|
p->init, 5,
|
||||||
|
p->payload,
|
||||||
|
session->fullpacket_payload_len,
|
||||||
|
&session->remote.mac_abstract);
|
||||||
|
}
|
||||||
|
|
||||||
/* Compare the calculated hash with the MAC we just read from
|
/* Compare the calculated hash with the MAC we just read from
|
||||||
* the network. The read one is at the very end of the payload
|
* the network. The read one is at the very end of the payload
|
||||||
* buffer. Note that 'payload_len' here is the packet_length
|
* buffer. Note that 'payload_len' here is the packet_length
|
||||||
* field which includes the padding but not the MAC.
|
* field which includes the padding but not the MAC.
|
||||||
*/
|
*/
|
||||||
if(memcmp(macbuf, p->payload + session->fullpacket_payload_len,
|
if(memcmp(macbuf, p->payload + p->total_num - mac_len, mac_len)) {
|
||||||
session->remote.mac->mac_len)) {
|
_libssh2_debug((session, LIBSSH2_TRACE_SOCKET,
|
||||||
|
"Failed MAC check"));
|
||||||
session->fullpacket_macstate = LIBSSH2_MAC_INVALID;
|
session->fullpacket_macstate = LIBSSH2_MAC_INVALID;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if(etm) {
|
||||||
|
/* MAC was ok and we start by decrypting the first block that
|
||||||
|
contains padding length since this allows us to decrypt
|
||||||
|
all other blocks to the right location in memory
|
||||||
|
avoiding moving a larger block of memory one byte. */
|
||||||
|
unsigned char first_block[MAX_BLOCKSIZE];
|
||||||
|
ssize_t decrypt_size;
|
||||||
|
unsigned char *decrypt_buffer;
|
||||||
|
int blocksize = session->remote.crypt->blocksize;
|
||||||
|
|
||||||
|
rc = decrypt(session, p->payload + 4,
|
||||||
|
first_block, blocksize, FIRST_BLOCK);
|
||||||
|
if(rc) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we need buffer for decrypt */
|
||||||
|
decrypt_size = p->total_num - mac_len - 4;
|
||||||
|
decrypt_buffer = LIBSSH2_ALLOC(session, decrypt_size);
|
||||||
|
if(!decrypt_buffer) {
|
||||||
|
return LIBSSH2_ERROR_ALLOC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* grab padding length and copy anything else
|
||||||
|
into target buffer */
|
||||||
|
p->padding_length = first_block[0];
|
||||||
|
if(blocksize > 1) {
|
||||||
|
memcpy(decrypt_buffer, first_block + 1, blocksize - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* decrypt all other blocks packet */
|
||||||
|
if(blocksize < decrypt_size) {
|
||||||
|
rc = decrypt(session, p->payload + blocksize + 4,
|
||||||
|
decrypt_buffer + blocksize - 1,
|
||||||
|
decrypt_size - blocksize, LAST_BLOCK);
|
||||||
|
if(rc) {
|
||||||
|
LIBSSH2_FREE(session, decrypt_buffer);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* replace encrypted payload with plain text payload */
|
||||||
|
LIBSSH2_FREE(session, p->payload);
|
||||||
|
p->payload = decrypt_buffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,6 +408,7 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
|
|||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
int etm;
|
||||||
if(session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) {
|
if(session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) {
|
||||||
return LIBSSH2_ERROR_SOCKET_DISCONNECT;
|
return LIBSSH2_ERROR_SOCKET_DISCONNECT;
|
||||||
}
|
}
|
||||||
@ -361,6 +422,8 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
|
|||||||
make the checks below work fine still */
|
make the checks below work fine still */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
etm = encrypted && session->local.mac ? session->local.mac->etm : 0;
|
||||||
|
|
||||||
/* read/use a whole big chunk into a temporary area stored in
|
/* read/use a whole big chunk into a temporary area stored in
|
||||||
the LIBSSH2_SESSION struct. We will decrypt data from that
|
the LIBSSH2_SESSION struct. We will decrypt data from that
|
||||||
buffer into the packet buffer so this temp one doesn't have
|
buffer into the packet buffer so this temp one doesn't have
|
||||||
@ -429,45 +492,55 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
|
|||||||
(5 bytes) packet length and padding length
|
(5 bytes) packet length and padding length
|
||||||
fields */
|
fields */
|
||||||
|
|
||||||
|
/* packet length is not encrypted in encode-then-mac mode
|
||||||
|
and we donøt need to decrypt first block */
|
||||||
|
ssize_t required_size = etm ? 4 : blocksize;
|
||||||
|
|
||||||
/* No payload package area allocated yet. To know the
|
/* No payload package area allocated yet. To know the
|
||||||
size of this payload, we need to decrypt the first
|
size of this payload, we need enough to decrypt the first
|
||||||
blocksize data. */
|
blocksize data. */
|
||||||
|
|
||||||
if(numbytes < blocksize) {
|
if(numbytes < required_size) {
|
||||||
/* we can't act on anything less than blocksize, but this
|
/* we can't act on anything less than blocksize, but this
|
||||||
check is only done for the initial block since once we have
|
check is only done for the initial block since once we have
|
||||||
got the start of a block we can in fact deal with fractions
|
got the start of a block we can in fact deal with fractions
|
||||||
*/
|
*/
|
||||||
session->socket_block_directions |=
|
session->socket_block_directions |=
|
||||||
LIBSSH2_SESSION_BLOCK_INBOUND;
|
LIBSSH2_SESSION_BLOCK_INBOUND;
|
||||||
return LIBSSH2_ERROR_EAGAIN;
|
return LIBSSH2_ERROR_EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(encrypted) {
|
if(etm) {
|
||||||
/* first decrypted block */
|
p->packet_length = _libssh2_ntohu32(&p->buf[p->readidx]);
|
||||||
rc = decrypt(session, &p->buf[p->readidx],
|
|
||||||
block, blocksize, FIRST_BLOCK);
|
|
||||||
if(rc != LIBSSH2_ERROR_NONE) {
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
/* Save the first 5 bytes of the decrypted package, to be
|
|
||||||
used in the hash calculation later down.
|
|
||||||
This is ignored in the INTEGRATED_MAC case. */
|
|
||||||
memcpy(p->init, block, 5);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* the data is plain, just copy it verbatim to
|
if(encrypted) {
|
||||||
the working block buffer */
|
/* first decrypted block */
|
||||||
memcpy(block, &p->buf[p->readidx], blocksize);
|
rc = decrypt(session, &p->buf[p->readidx],
|
||||||
|
block, blocksize, FIRST_BLOCK);
|
||||||
|
if(rc != LIBSSH2_ERROR_NONE) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
/* Save the first 5 bytes of the decrypted package, to be
|
||||||
|
used in the hash calculation later down.
|
||||||
|
This is ignored in the INTEGRATED_MAC case. */
|
||||||
|
memcpy(p->init, block, 5);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* the data is plain, just copy it verbatim to
|
||||||
|
the working block buffer */
|
||||||
|
memcpy(block, &p->buf[p->readidx], blocksize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* advance the read pointer */
|
||||||
|
p->readidx += blocksize;
|
||||||
|
|
||||||
|
/* we now have the initial blocksize bytes decrypted,
|
||||||
|
* and we can extract packet and padding length from it
|
||||||
|
*/
|
||||||
|
p->packet_length = _libssh2_ntohu32(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* advance the read pointer */
|
|
||||||
p->readidx += blocksize;
|
|
||||||
|
|
||||||
/* we now have the initial blocksize bytes decrypted,
|
|
||||||
* and we can extract packet and padding length from it
|
|
||||||
*/
|
|
||||||
p->packet_length = _libssh2_ntohu32(block);
|
|
||||||
if(p->packet_length < 1) {
|
if(p->packet_length < 1) {
|
||||||
return LIBSSH2_ERROR_DECRYPT;
|
return LIBSSH2_ERROR_DECRYPT;
|
||||||
}
|
}
|
||||||
@ -475,19 +548,27 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
|
|||||||
return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
|
return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* padding_length has not been authenticated yet, but it won't
|
if(etm) {
|
||||||
actually be used (except for the sanity check immediately
|
/* we collect entire undecrypted packet including the
|
||||||
following) until after the entire packet is authenticated,
|
packet length field that we run MAC over */
|
||||||
so this is safe. */
|
total_num = 4 + p->packet_length +
|
||||||
p->padding_length = block[4];
|
session->remote.mac->mac_len;
|
||||||
if(p->padding_length > p->packet_length - 1) {
|
|
||||||
return LIBSSH2_ERROR_DECRYPT;
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
/* padding_length has not been authenticated yet, but it won't
|
||||||
|
actually be used (except for the sanity check immediately
|
||||||
|
following) until after the entire packet is authenticated,
|
||||||
|
so this is safe. */
|
||||||
|
p->padding_length = block[4];
|
||||||
|
if(p->padding_length > p->packet_length - 1) {
|
||||||
|
return LIBSSH2_ERROR_DECRYPT;
|
||||||
|
}
|
||||||
|
|
||||||
/* total_num is the number of bytes following the initial
|
/* total_num is the number of bytes following the initial
|
||||||
(5 bytes) packet length and padding length fields */
|
(5 bytes) packet length and padding length fields */
|
||||||
total_num = p->packet_length - 1 +
|
total_num = p->packet_length - 1 +
|
||||||
(encrypted ? session->remote.mac->mac_len : 0);
|
(encrypted ? session->remote.mac->mac_len : 0);
|
||||||
|
}
|
||||||
|
|
||||||
/* RFC4253 section 6.1 Maximum Packet Length says:
|
/* RFC4253 section 6.1 Maximum Packet Length says:
|
||||||
*
|
*
|
||||||
@ -511,13 +592,17 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
|
|||||||
/* init write pointer to start of payload buffer */
|
/* init write pointer to start of payload buffer */
|
||||||
p->wptr = p->payload;
|
p->wptr = p->payload;
|
||||||
|
|
||||||
if(blocksize > 5) {
|
if(!etm && blocksize > 5) {
|
||||||
/* copy the data from index 5 to the end of
|
/* copy the data from index 5 to the end of
|
||||||
the blocksize from the temporary buffer to
|
the blocksize from the temporary buffer to
|
||||||
the start of the decrypted buffer */
|
the start of the decrypted buffer */
|
||||||
if(blocksize - 5 <= (int) total_num) {
|
if(blocksize - 5 <= (int) total_num) {
|
||||||
memcpy(p->wptr, &block[5], blocksize - 5);
|
memcpy(p->wptr, &block[5], blocksize - 5);
|
||||||
p->wptr += blocksize - 5; /* advance write pointer */
|
p->wptr += blocksize - 5; /* advance write pointer */
|
||||||
|
if(etm) {
|
||||||
|
/* advance past unencrypted packet length */
|
||||||
|
p->wptr += 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(p->payload)
|
if(p->payload)
|
||||||
@ -531,7 +616,8 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
|
|||||||
p->data_num = p->wptr - p->payload;
|
p->data_num = p->wptr - p->payload;
|
||||||
|
|
||||||
/* we already dealt with a blocksize worth of data */
|
/* we already dealt with a blocksize worth of data */
|
||||||
numbytes -= blocksize;
|
if(!etm)
|
||||||
|
numbytes -= blocksize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* how much there is left to add to the current payload
|
/* how much there is left to add to the current payload
|
||||||
@ -544,7 +630,7 @@ int _libssh2_transport_read(LIBSSH2_SESSION * session)
|
|||||||
numbytes = remainpack;
|
numbytes = remainpack;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(encrypted) {
|
if(encrypted && !etm) {
|
||||||
/* At the end of the incoming stream, there is a MAC,
|
/* At the end of the incoming stream, there is a MAC,
|
||||||
and we don't want to decrypt that since we need it
|
and we don't want to decrypt that since we need it
|
||||||
"raw". We MUST however decrypt the padding data
|
"raw". We MUST however decrypt the padding data
|
||||||
@ -772,11 +858,12 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
|
|||||||
struct transportpacket *p = &session->packet;
|
struct transportpacket *p = &session->packet;
|
||||||
int encrypted;
|
int encrypted;
|
||||||
int compressed;
|
int compressed;
|
||||||
|
int etm;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
int rc;
|
int rc;
|
||||||
const unsigned char *orgdata = data;
|
const unsigned char *orgdata = data;
|
||||||
size_t orgdata_len = data_len;
|
size_t orgdata_len = data_len;
|
||||||
size_t crypt_offset;
|
size_t crypt_offset, etm_crypt_offset;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the last read operation was interrupted in the middle of a key
|
* If the last read operation was interrupted in the middle of a key
|
||||||
@ -814,6 +901,8 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
|
|||||||
|
|
||||||
encrypted = (session->state & LIBSSH2_STATE_NEWKEYS) ? 1 : 0;
|
encrypted = (session->state & LIBSSH2_STATE_NEWKEYS) ? 1 : 0;
|
||||||
|
|
||||||
|
etm = encrypted && session->local.mac ? session->local.mac->etm : 0;
|
||||||
|
|
||||||
compressed = session->local.comp &&
|
compressed = session->local.comp &&
|
||||||
session->local.comp->compress &&
|
session->local.comp->compress &&
|
||||||
((session->state & LIBSSH2_STATE_AUTHENTICATED) ||
|
((session->state & LIBSSH2_STATE_AUTHENTICATED) ||
|
||||||
@ -875,8 +964,11 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
|
|||||||
|
|
||||||
packet_length = data_len + 1 + 4; /* 1 is for padding_length field
|
packet_length = data_len + 1 + 4; /* 1 is for padding_length field
|
||||||
4 for the packet_length field */
|
4 for the packet_length field */
|
||||||
/* subtract 4 bytes of the packet_length field when padding AES-GCM */
|
/* subtract 4 bytes of the packet_length field when padding AES-GCM
|
||||||
crypt_offset = (encrypted && CRYPT_FLAG_R(session, PKTLEN_AAD)) ? 4 : 0;
|
or with ETM */
|
||||||
|
crypt_offset = (etm || (encrypted && CRYPT_FLAG_R(session, PKTLEN_AAD)))
|
||||||
|
? 4 : 0;
|
||||||
|
etm_crypt_offset = etm ? 4 : 0;
|
||||||
|
|
||||||
/* at this point we have it all except the padding */
|
/* at this point we have it all except the padding */
|
||||||
|
|
||||||
@ -928,7 +1020,7 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
|
|||||||
fields except the MAC field itself. This is skipped in the
|
fields except the MAC field itself. This is skipped in the
|
||||||
INTEGRATED_MAC case, where the crypto algorithm also does its
|
INTEGRATED_MAC case, where the crypto algorithm also does its
|
||||||
own hash. */
|
own hash. */
|
||||||
if(!CRYPT_FLAG_R(session, INTEGRATED_MAC)) {
|
if(!etm && !CRYPT_FLAG_R(session, INTEGRATED_MAC)) {
|
||||||
session->local.mac->hash(session, p->outbuf + packet_length,
|
session->local.mac->hash(session, p->outbuf + packet_length,
|
||||||
session->local.seqno, p->outbuf,
|
session->local.seqno, p->outbuf,
|
||||||
packet_length, NULL, 0,
|
packet_length, NULL, 0,
|
||||||
@ -940,23 +1032,23 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
|
|||||||
/* Some crypto back-ends could handle a single crypt() call for
|
/* Some crypto back-ends could handle a single crypt() call for
|
||||||
encryption, but (presumably) others cannot, so break it up
|
encryption, but (presumably) others cannot, so break it up
|
||||||
into blocksize-sized chunks to satisfy them all. */
|
into blocksize-sized chunks to satisfy them all. */
|
||||||
for(i = 0; i < packet_length;
|
for(i = etm_crypt_offset; i < packet_length;
|
||||||
i += session->local.crypt->blocksize) {
|
i += session->local.crypt->blocksize) {
|
||||||
unsigned char *ptr = &p->outbuf[i];
|
unsigned char *ptr = &p->outbuf[i];
|
||||||
size_t bsize = LIBSSH2_MIN(session->local.crypt->blocksize,
|
size_t bsize = LIBSSH2_MIN(session->local.crypt->blocksize,
|
||||||
(int)(packet_length-i));
|
(int)(packet_length-i));
|
||||||
/* The INTEGRATED_MAC case always has an extra call below,
|
/* The INTEGRATED_MAC case always has an extra call below, so it
|
||||||
so it will never be LAST_BLOCK up here. */
|
will never be LAST_BLOCK up here. */
|
||||||
int firstlast = i == 0 ? FIRST_BLOCK :
|
int firstlast = i == 0 ? FIRST_BLOCK :
|
||||||
(!CRYPT_FLAG_L(session, INTEGRATED_MAC)
|
(!CRYPT_FLAG_L(session, INTEGRATED_MAC)
|
||||||
&& (i == packet_length - session->local.crypt->blocksize)
|
&& (i == packet_length - session->local.crypt->blocksize)
|
||||||
? LAST_BLOCK: MIDDLE_BLOCK);
|
? LAST_BLOCK: MIDDLE_BLOCK);
|
||||||
/* In the AAD case, the last block would be only 4 bytes
|
/* In the AAD case, the last block would be only 4 bytes because
|
||||||
because everything is offset by 4 since the initial
|
everything is offset by 4 since the initial packet_length isn't
|
||||||
packet_length isn't encrypted. In this case, combine that
|
encrypted. In this case, combine that last short packet with the
|
||||||
last short packet with the previous one since AES-GCM
|
previous one since AES-GCM crypt() assumes that the entire MAC
|
||||||
crypt() assumes that the entire MAC is available in that
|
is available in that packet so it can set that to the
|
||||||
packet so it can set that to the authentication tag. */
|
authentication tag. */
|
||||||
if(!CRYPT_FLAG_L(session, INTEGRATED_MAC))
|
if(!CRYPT_FLAG_L(session, INTEGRATED_MAC))
|
||||||
if(i > packet_length - 2*bsize) {
|
if(i > packet_length - 2*bsize) {
|
||||||
/* increase the final block size */
|
/* increase the final block size */
|
||||||
@ -964,25 +1056,38 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
|
|||||||
/* advance the loop counter by the extra amount */
|
/* advance the loop counter by the extra amount */
|
||||||
i += bsize - session->local.crypt->blocksize;
|
i += bsize - session->local.crypt->blocksize;
|
||||||
}
|
}
|
||||||
|
_libssh2_debug((session, LIBSSH2_TRACE_SOCKET,
|
||||||
|
"crypting bytes %d-%d", i,
|
||||||
|
i + session->local.crypt->blocksize - 1));
|
||||||
if(session->local.crypt->crypt(session, ptr,
|
if(session->local.crypt->crypt(session, ptr,
|
||||||
bsize,
|
bsize,
|
||||||
&session->local.crypt_abstract,
|
&session->local.crypt_abstract,
|
||||||
firstlast))
|
firstlast))
|
||||||
return LIBSSH2_ERROR_ENCRYPT; /* encryption failure */
|
return LIBSSH2_ERROR_ENCRYPT; /* encryption failure */
|
||||||
}
|
}
|
||||||
/* Call crypt() one last time so it can be filled in with
|
/* Call crypt() one last time so it can be filled in with the MAC */
|
||||||
the MAC */
|
|
||||||
if(CRYPT_FLAG_L(session, INTEGRATED_MAC)) {
|
if(CRYPT_FLAG_L(session, INTEGRATED_MAC)) {
|
||||||
int authlen = session->local.mac->mac_len;
|
int authlen = session->local.mac->mac_len;
|
||||||
assert((size_t)total_length <=
|
assert((size_t)total_length <=
|
||||||
packet_length + session->local.crypt->blocksize);
|
packet_length + session->local.crypt->blocksize);
|
||||||
if(session->local.crypt->crypt(session,
|
if(session->local.crypt->crypt(session, &p->outbuf[packet_length],
|
||||||
&p->outbuf[packet_length],
|
|
||||||
authlen,
|
authlen,
|
||||||
&session->local.crypt_abstract,
|
&session->local.crypt_abstract,
|
||||||
LAST_BLOCK))
|
LAST_BLOCK))
|
||||||
return LIBSSH2_ERROR_ENCRYPT; /* encryption failure */
|
return LIBSSH2_ERROR_ENCRYPT; /* encryption failure */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(etm) {
|
||||||
|
/* Calculate MAC hash. Put the output at index packet_length,
|
||||||
|
since that size includes the whole packet. The MAC is
|
||||||
|
calculated on the entire packet (length plain the rest
|
||||||
|
encrypted), including all fields except the MAC field
|
||||||
|
itself. */
|
||||||
|
session->local.mac->hash(session, p->outbuf + packet_length,
|
||||||
|
session->local.seqno, p->outbuf,
|
||||||
|
packet_length, NULL, 0,
|
||||||
|
&session->local.mac_abstract);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
session->local.seqno++;
|
session->local.seqno++;
|
||||||
|
@ -117,6 +117,9 @@ foreach(test
|
|||||||
hmac-sha1-96
|
hmac-sha1-96
|
||||||
hmac-sha2-256
|
hmac-sha2-256
|
||||||
hmac-sha2-512
|
hmac-sha2-512
|
||||||
|
hmac-sha1-etm@openssh.com
|
||||||
|
hmac-sha2-256-etm@openssh.com
|
||||||
|
hmac-sha2-512-etm@openssh.com
|
||||||
)
|
)
|
||||||
add_test(NAME test_${test} COMMAND "$<TARGET_FILE:test_read>")
|
add_test(NAME test_${test} COMMAND "$<TARGET_FILE:test_read>")
|
||||||
set_tests_properties(test_${test} PROPERTIES ENVIRONMENT "FIXTURE_TEST_MAC=${test}")
|
set_tests_properties(test_${test} PROPERTIES ENVIRONMENT "FIXTURE_TEST_MAC=${test}")
|
||||||
|
@ -115,4 +115,7 @@ EXTRA_DIST = \
|
|||||||
test_read_hmac-sha1-96 \
|
test_read_hmac-sha1-96 \
|
||||||
test_read_hmac-sha2-256 \
|
test_read_hmac-sha2-256 \
|
||||||
test_read_hmac-sha2-512 \
|
test_read_hmac-sha2-512 \
|
||||||
test_read_rijndael-cbc
|
test_read_rijndael-cbc \
|
||||||
|
test_read_hmac-sha1-etm@openssh.com \
|
||||||
|
test_read_hmac-sha2-256-etm@openssh.com \
|
||||||
|
test_read_hmac-sha2-512-etm@openssh.com
|
||||||
|
@ -310,7 +310,7 @@ int main(void)
|
|||||||
|
|
||||||
for(i = 0; i < FAILED_MALLOC_TEST_CASES_LEN; i++) {
|
for(i = 0; i < FAILED_MALLOC_TEST_CASES_LEN; i++) {
|
||||||
int tc = i + TEST_CASES_LEN + 1;
|
int tc = i + TEST_CASES_LEN + 1;
|
||||||
int malloc_call_num = 5 + i;
|
int malloc_call_num = 3 + i;
|
||||||
test_case(tc,
|
test_case(tc,
|
||||||
failed_malloc_test_cases[i].data,
|
failed_malloc_test_cases[i].data,
|
||||||
failed_malloc_test_cases[i].data_len,
|
failed_malloc_test_cases[i].data_len,
|
||||||
|
Reference in New Issue
Block a user