diff options
author | Dirkjan Bussink <d.bussink@gmail.com> | 2014-04-20 10:04:21 +0000 |
---|---|---|
committer | Andreas Schneider <asn@cryptomilk.org> | 2014-04-22 10:56:46 +0200 |
commit | 164b8e99cce70c83d2ef3e2c2b832a514a0ac908 (patch) | |
tree | d748e7afc7447354c9493bb7957f264d1140affa /src | |
parent | 4a089026647073be32ddb0885c12f47496bc709b (diff) | |
download | libssh-164b8e99cce70c83d2ef3e2c2b832a514a0ac908.tar.gz libssh-164b8e99cce70c83d2ef3e2c2b832a514a0ac908.tar.xz libssh-164b8e99cce70c83d2ef3e2c2b832a514a0ac908.zip |
Add logic to support SHA2 HMAC algorithms
BUG: https://red.libssh.org/issues/91
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Diffstat (limited to 'src')
-rw-r--r-- | src/dh.c | 112 | ||||
-rw-r--r-- | src/packet.c | 19 | ||||
-rw-r--r-- | src/packet_crypt.c | 16 | ||||
-rw-r--r-- | src/wrapper.c | 17 |
4 files changed, 91 insertions, 73 deletions
@@ -864,8 +864,10 @@ int hashbufin_add_cookie(ssh_session session, unsigned char *cookie) { } static int generate_one_key(ssh_string k, - struct ssh_crypto_struct *crypto, unsigned char *output, char letter) { + struct ssh_crypto_struct *crypto, unsigned char **output, char letter, size_t requested_size) { ssh_mac_ctx ctx; + unsigned char *tmp; + size_t size = crypto->digest_len; ctx=ssh_mac_ctx_init(crypto->mac_type); if (ctx == NULL) { @@ -876,16 +878,33 @@ static int generate_one_key(ssh_string k, ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len); ssh_mac_update(ctx, &letter, 1); ssh_mac_update(ctx, crypto->session_id, crypto->digest_len); - ssh_mac_final(output, ctx); + ssh_mac_final(*output, ctx); + + while(requested_size > size) { + tmp = realloc(*output, size + crypto->digest_len); + if (tmp == NULL) { + return -1; + } + *output = tmp; + + ctx = ssh_mac_ctx_init(crypto->mac_type); + if (ctx == NULL) { + return -1; + } + ssh_mac_update(ctx, k, ssh_string_len(k) + 4); + ssh_mac_update(ctx, crypto->secret_hash, + crypto->digest_len); + ssh_mac_update(ctx, tmp, size); + ssh_mac_final(tmp + size, ctx); + size += crypto->digest_len; + } return 0; } int generate_session_keys(ssh_session session) { ssh_string k_string = NULL; - ssh_mac_ctx ctx = NULL; struct ssh_crypto_struct *crypto = session->next_crypto; - unsigned char *tmp; int rc = -1; k_string = make_bignum_string(crypto->k); @@ -909,96 +928,71 @@ int generate_session_keys(ssh_session session) { /* IV */ if (session->client) { - if (generate_one_key(k_string, crypto, crypto->encryptIV, 'A') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->encryptIV, 'A', crypto->digest_len); + if (rc < 0) { goto error; } - if (generate_one_key(k_string, crypto, crypto->decryptIV, 'B') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->decryptIV, 'B', crypto->digest_len); + if (rc < 0) { goto error; } } else { - if (generate_one_key(k_string, crypto, crypto->decryptIV, 'A') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->decryptIV, 'A', crypto->digest_len); + if (rc < 0) { goto error; } - if (generate_one_key(k_string, crypto, crypto->encryptIV, 'B') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->encryptIV, 'B', crypto->digest_len); + if (rc < 0) { goto error; } } if (session->client) { - if (generate_one_key(k_string, crypto, crypto->encryptkey, 'C') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->encryptkey, 'C', crypto->out_cipher->keysize / 8); + if (rc < 0) { goto error; } - if (generate_one_key(k_string, crypto, crypto->decryptkey, 'D') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->decryptkey, 'D', crypto->in_cipher->keysize / 8); + if (rc < 0) { goto error; } } else { - if (generate_one_key(k_string, crypto, crypto->decryptkey, 'C') < 0) { - goto error; - } - if (generate_one_key(k_string, crypto, crypto->encryptkey, 'D') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->decryptkey, 'C', crypto->in_cipher->keysize / 8); + if (rc < 0) { goto error; } - } - - /* some ciphers need more than DIGEST_LEN bytes of input key */ - if (crypto->out_cipher->keysize > crypto->digest_len * 8) { - tmp = realloc(crypto->encryptkey, crypto->digest_len * 2); - if (tmp == NULL) { - goto error; - } - crypto->encryptkey = tmp; - - ctx = ssh_mac_ctx_init(crypto->mac_type); - if (ctx == NULL) { + rc = generate_one_key(k_string, crypto, &crypto->encryptkey, 'D', crypto->out_cipher->keysize / 8); + if (rc < 0) { goto error; } - ssh_mac_update(ctx, k_string, ssh_string_len(k_string) + 4); - ssh_mac_update(ctx, crypto->secret_hash, - crypto->digest_len); - ssh_mac_update(ctx, crypto->encryptkey, crypto->digest_len); - ssh_mac_final(crypto->encryptkey + crypto->digest_len, ctx); } - if (crypto->in_cipher->keysize > crypto->digest_len * 8) { - tmp = realloc(crypto->decryptkey, crypto->digest_len *2); - if (tmp == NULL) { - goto error; - } - crypto->decryptkey = tmp; - - if(crypto->decryptkey == NULL) - goto error; - ctx = ssh_mac_ctx_init(crypto->mac_type); - ssh_mac_update(ctx, k_string, ssh_string_len(k_string) + 4); - ssh_mac_update(ctx, crypto->secret_hash, - crypto->digest_len); - ssh_mac_update(ctx, crypto->decryptkey, crypto->digest_len); - ssh_mac_final(crypto->decryptkey + crypto->digest_len, ctx); - } if(session->client) { - if (generate_one_key(k_string, crypto, crypto->encryptMAC, 'E') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->encryptMAC, 'E', hmac_digest_len(crypto->out_hmac)); + if (rc < 0) { goto error; } - if (generate_one_key(k_string, crypto, crypto->decryptMAC, 'F') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->decryptMAC, 'F', hmac_digest_len(crypto->in_hmac)); + if (rc < 0) { goto error; } } else { - if (generate_one_key(k_string, crypto, crypto->decryptMAC, 'E') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->decryptMAC, 'E', hmac_digest_len(crypto->in_hmac)); + if (rc < 0) { goto error; } - if (generate_one_key(k_string, crypto, crypto->encryptMAC, 'F') < 0) { + rc = generate_one_key(k_string, crypto, &crypto->encryptMAC, 'F', hmac_digest_len(crypto->out_hmac)); + if (rc < 0) { goto error; } } #ifdef DEBUG_CRYPTO - ssh_print_hexa("Encrypt IV", crypto->encryptIV, SHA_DIGEST_LEN); - ssh_print_hexa("Decrypt IV", crypto->decryptIV, SHA_DIGEST_LEN); - ssh_print_hexa("Encryption key", crypto->encryptkey, - crypto->out_cipher->keysize); - ssh_print_hexa("Decryption key", crypto->decryptkey, - crypto->in_cipher->keysize); - ssh_print_hexa("Encryption MAC", crypto->encryptMAC, SHA_DIGEST_LEN); - ssh_print_hexa("Decryption MAC", crypto->decryptMAC, 20); + ssh_print_hexa("Encrypt IV", crypto->encryptIV, crypto->digest_len); + ssh_print_hexa("Decrypt IV", crypto->decryptIV, crypto->digest_len); + ssh_print_hexa("Encryption key", crypto->encryptkey, crypto->out_cipher->keysize / 8); + ssh_print_hexa("Decryption key", crypto->decryptkey, crypto->in_cipher->keysize / 8); + ssh_print_hexa("Encryption MAC", crypto->encryptMAC, hmac_digest_len(crypto->out_hmac)); + ssh_print_hexa("Decryption MAC", crypto->decryptMAC, hmac_digest_len(crypto->in_hmac)); #endif rc = 0; diff --git a/src/packet.c b/src/packet.c index 1e267275..78a512a5 100644 --- a/src/packet.c +++ b/src/packet.c @@ -48,8 +48,6 @@ #include "libssh/auth.h" #include "libssh/gssapi.h" -#define MACSIZE SHA_DIGEST_LEN - static ssh_packet_callback default_packet_handlers[]= { ssh_packet_disconnect_callback, // SSH2_MSG_DISCONNECT 1 ssh_packet_ignore_callback, // SSH2_MSG_IGNORE 2 @@ -146,9 +144,9 @@ 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); - int current_macsize = session->current_crypto ? MACSIZE : 0; - unsigned char mac[30] = {0}; + unsigned char mac[DIGEST_MAX_LEN] = {0}; char buffer[16] = {0}; + size_t current_macsize = 0; const uint8_t *packet; int to_be_read; int rc; @@ -156,6 +154,10 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) uint8_t padding; 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); + } + if (data == NULL) { goto error; } @@ -267,9 +269,9 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user) /* copy the last part from the incoming buffer */ packet = ((uint8_t *)data) + processed; - memcpy(mac, packet, MACSIZE); + memcpy(mac, packet, current_macsize); - rc = packet_hmac_verify(session, session->in_buffer, mac); + rc = 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; @@ -506,6 +508,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); + 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 }; @@ -558,7 +562,8 @@ static int packet_send2(ssh_session session) { hmac = packet_encrypt(session, buffer_get_rest(session->out_buffer), buffer_get_rest_len(session->out_buffer)); if (hmac) { - if (ssh_buffer_add_data(session->out_buffer, hmac, 20) < 0) { + rc = ssh_buffer_add_data(session->out_buffer, hmac, hmac_digest_len(hmac_type)); + if (rc < 0) { goto error; } } diff --git a/src/packet_crypt.c b/src/packet_crypt.c index cb73e414..4f6ba176 100644 --- a/src/packet_crypt.c +++ b/src/packet_crypt.c @@ -92,6 +92,7 @@ unsigned char *packet_encrypt(ssh_session session, void *data, uint32_t len) { char *out = NULL; unsigned int finallen; uint32_t seq; + enum ssh_hmac_e type; assert(len); @@ -107,6 +108,7 @@ unsigned char *packet_encrypt(ssh_session session, void *data, uint32_t len) { return NULL; } + type = session->current_crypto->out_hmac; seq = ntohl(session->send_seq); crypto = session->current_crypto->out_cipher; @@ -117,7 +119,7 @@ unsigned char *packet_encrypt(ssh_session session, void *data, uint32_t len) { } if (session->version == 2) { - ctx = hmac_init(session->current_crypto->encryptMAC,20,SSH_HMAC_SHA1); + ctx = hmac_init(session->current_crypto->encryptMAC, hmac_digest_len(type), type); if (ctx == NULL) { SAFE_FREE(out); return NULL; @@ -126,11 +128,11 @@ unsigned char *packet_encrypt(ssh_session session, void *data, uint32_t len) { hmac_update(ctx,data,len); hmac_final(ctx,session->current_crypto->hmacbuf,&finallen); #ifdef DEBUG_CRYPTO - ssh_print_hexa("mac: ",data,len); - if (finallen != 20) { + 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, 20); + ssh_print_hexa("Packet hmac", session->current_crypto->hmacbuf, hmac_digest_len(type)); #endif } @@ -160,13 +162,13 @@ unsigned char *packet_encrypt(ssh_session session, void *data, uint32_t len) { * occurred. */ int packet_hmac_verify(ssh_session session, ssh_buffer buffer, - unsigned char *mac) { - unsigned char hmacbuf[EVP_MAX_MD_SIZE] = {0}; + unsigned char *mac, enum ssh_hmac_e type) { + unsigned char hmacbuf[DIGEST_MAX_LEN] = {0}; HMACCTX ctx; unsigned int len; uint32_t seq; - ctx = hmac_init(session->current_crypto->decryptMAC, 20, SSH_HMAC_SHA1); + ctx = hmac_init(session->current_crypto->decryptMAC, hmac_digest_len(type), type); if (ctx == NULL) { return -1; } diff --git a/src/wrapper.c b/src/wrapper.c index 94175d0e..3b411a4c 100644 --- a/src/wrapper.c +++ b/src/wrapper.c @@ -48,6 +48,23 @@ #include "libssh/wrapper.h" #include "libssh/pki.h" +size_t hmac_digest_len(enum ssh_hmac_e type) { + switch(type) { + case SSH_HMAC_SHA1: + return SHA_DIGEST_LEN; + case SSH_HMAC_SHA256: + return SHA256_DIGEST_LEN; + case SSH_HMAC_SHA384: + return SHA384_DIGEST_LEN; + case SSH_HMAC_SHA512: + return SHA512_DIGEST_LEN; + case SSH_HMAC_MD5: + return MD5_DIGEST_LEN; + default: + return 0; + } +} + /* it allocates a new cipher structure based on its offset into the global table */ static struct ssh_cipher_struct *cipher_new(int offset) { struct ssh_cipher_struct *cipher = NULL; |