Add logic to support SHA2 HMAC algorithms
BUG: https://red.libssh.org/issues/91 Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Этот коммит содержится в:
родитель
4a08902664
Коммит
164b8e99cc
@ -46,6 +46,8 @@
|
||||
#include "libssh/kex.h"
|
||||
#include "libssh/curve25519.h"
|
||||
|
||||
#define DIGEST_MAX_LEN 64
|
||||
|
||||
enum ssh_key_exchange_e {
|
||||
/* diffie-hellman-group1-sha1 */
|
||||
SSH_KEX_DH_GROUP1_SHA1=1,
|
||||
@ -79,8 +81,10 @@ struct ssh_crypto_struct {
|
||||
unsigned char *encryptkey;
|
||||
unsigned char *encryptMAC;
|
||||
unsigned char *decryptMAC;
|
||||
unsigned char hmacbuf[EVP_MAX_MD_SIZE];
|
||||
unsigned char hmacbuf[DIGEST_MAX_LEN];
|
||||
struct ssh_cipher_struct *in_cipher, *out_cipher; /* the cipher structures/objects */
|
||||
enum ssh_hmac_e in_hmac, out_hmac; /* the MAC algorithms used */
|
||||
|
||||
ssh_string server_pubkey;
|
||||
const char *server_pubkey_type;
|
||||
int do_compress_out; /* idem */
|
||||
|
@ -21,6 +21,8 @@
|
||||
#ifndef PACKET_H_
|
||||
#define PACKET_H_
|
||||
|
||||
#include "libssh/wrapper.h"
|
||||
|
||||
struct ssh_socket_struct;
|
||||
|
||||
/* this structure should go someday */
|
||||
@ -82,6 +84,6 @@ unsigned char *packet_encrypt(ssh_session session,
|
||||
void *packet,
|
||||
unsigned int len);
|
||||
int packet_hmac_verify(ssh_session session,ssh_buffer buffer,
|
||||
unsigned char *mac);
|
||||
unsigned char *mac, enum ssh_hmac_e type);
|
||||
|
||||
#endif /* PACKET_H_ */
|
||||
|
@ -82,6 +82,7 @@ void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx);
|
||||
HMACCTX hmac_init(const void *key,int len, enum ssh_hmac_e type);
|
||||
void hmac_update(HMACCTX c, const void *data, unsigned long len);
|
||||
void hmac_final(HMACCTX ctx,unsigned char *hashmacbuf,unsigned int *len);
|
||||
size_t hmac_digest_len(enum ssh_hmac_e type);
|
||||
|
||||
int crypt_set_algorithms(ssh_session session, enum ssh_des_e des_type);
|
||||
int crypt_set_algorithms_server(ssh_session session);
|
||||
|
112
src/dh.c
112
src/dh.c
@ -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) {
|
||||
rc = generate_one_key(k_string, crypto, &crypto->decryptkey, 'C', crypto->in_cipher->keysize / 8);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
if (generate_one_key(k_string, crypto, crypto->encryptkey, 'D') < 0) {
|
||||
rc = generate_one_key(k_string, crypto, &crypto->encryptkey, 'D', crypto->out_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) {
|
||||
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;
|
||||
|
19
src/packet.c
19
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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
Загрузка…
Ссылка в новой задаче
Block a user