dh: move unrelated functions out of dh.c
Signed-off-by: Aris Adamantiadis <aris@0xbadc0de.be> Reviewed-by: Jakub Jelen <jjelen@redhat.com> Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Этот коммит содержится в:
родитель
e42a423a24
Коммит
43a4f86b6e
@ -51,12 +51,6 @@ ssh_key ssh_dh_get_next_server_publickey(ssh_session session);
|
||||
int ssh_dh_get_next_server_publickey_blob(ssh_session session,
|
||||
ssh_string *pubkey_blob);
|
||||
|
||||
int ssh_make_sessionid(ssh_session session);
|
||||
/* add data for the final cookie */
|
||||
int ssh_hashbufin_add_cookie(ssh_session session, unsigned char *cookie);
|
||||
int ssh_hashbufout_add_cookie(ssh_session session);
|
||||
int ssh_generate_session_keys(ssh_session session);
|
||||
|
||||
#ifdef WITH_SERVER
|
||||
void ssh_server_dh_init(ssh_session session);
|
||||
#endif /* WITH_SERVER */
|
||||
|
@ -48,5 +48,10 @@ const char *ssh_kex_get_description(uint32_t algo);
|
||||
char *ssh_client_select_hostkeys(ssh_session session);
|
||||
int ssh_send_rekex(ssh_session session);
|
||||
int server_set_kex(ssh_session session);
|
||||
int ssh_make_sessionid(ssh_session session);
|
||||
/* add data for the final cookie */
|
||||
int ssh_hashbufin_add_cookie(ssh_session session, unsigned char *cookie);
|
||||
int ssh_hashbufout_add_cookie(ssh_session session);
|
||||
int ssh_generate_session_keys(ssh_session session);
|
||||
|
||||
#endif /* KEX_H_ */
|
||||
|
708
src/dh.c
708
src/dh.c
@ -921,531 +921,12 @@ error:
|
||||
|
||||
#endif /* WITH_SERVER */
|
||||
|
||||
int ssh_make_sessionid(ssh_session session) {
|
||||
ssh_string num = NULL;
|
||||
ssh_buffer server_hash = NULL;
|
||||
ssh_buffer client_hash = NULL;
|
||||
ssh_buffer buf = NULL;
|
||||
ssh_string server_pubkey_blob = NULL;
|
||||
int rc = SSH_ERROR;
|
||||
|
||||
buf = ssh_buffer_new();
|
||||
if (buf == NULL) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_pack(buf,
|
||||
"ss",
|
||||
session->clientbanner,
|
||||
session->serverbanner);
|
||||
if (rc == SSH_ERROR) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (session->client) {
|
||||
server_hash = session->in_hashbuf;
|
||||
client_hash = session->out_hashbuf;
|
||||
} else {
|
||||
server_hash = session->out_hashbuf;
|
||||
client_hash = session->in_hashbuf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the two final fields for the KEXINIT message (RFC 4253 7.1):
|
||||
*
|
||||
* boolean first_kex_packet_follows
|
||||
* uint32 0 (reserved for future extension)
|
||||
*/
|
||||
rc = ssh_buffer_add_u8(server_hash, 0);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
rc = ssh_buffer_add_u32(server_hash, 0);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* These fields are handled for the server case in ssh_packet_kexinit. */
|
||||
if (session->client) {
|
||||
rc = ssh_buffer_add_u8(client_hash, 0);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
rc = ssh_buffer_add_u32(client_hash, 0);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
rc = ssh_dh_get_next_server_publickey_blob(session, &server_pubkey_blob);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_pack(buf,
|
||||
"dPdPS",
|
||||
ssh_buffer_get_len(client_hash),
|
||||
ssh_buffer_get_len(client_hash),
|
||||
ssh_buffer_get(client_hash),
|
||||
ssh_buffer_get_len(server_hash),
|
||||
ssh_buffer_get_len(server_hash),
|
||||
ssh_buffer_get(server_hash),
|
||||
server_pubkey_blob);
|
||||
ssh_string_free(server_pubkey_blob);
|
||||
if(rc != SSH_OK){
|
||||
goto error;
|
||||
}
|
||||
|
||||
switch(session->next_crypto->kex_type) {
|
||||
case SSH_KEX_DH_GROUP1_SHA1:
|
||||
case SSH_KEX_DH_GROUP14_SHA1:
|
||||
case SSH_KEX_DH_GROUP16_SHA512:
|
||||
case SSH_KEX_DH_GROUP18_SHA512:
|
||||
rc = ssh_buffer_pack(buf,
|
||||
"BB",
|
||||
session->next_crypto->e,
|
||||
session->next_crypto->f);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
#ifdef HAVE_ECDH
|
||||
case SSH_KEX_ECDH_SHA2_NISTP256:
|
||||
case SSH_KEX_ECDH_SHA2_NISTP384:
|
||||
case SSH_KEX_ECDH_SHA2_NISTP521:
|
||||
if (session->next_crypto->ecdh_client_pubkey == NULL ||
|
||||
session->next_crypto->ecdh_server_pubkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "ECDH parameted missing");
|
||||
goto error;
|
||||
}
|
||||
rc = ssh_buffer_pack(buf,
|
||||
"SS",
|
||||
session->next_crypto->ecdh_client_pubkey,
|
||||
session->next_crypto->ecdh_server_pubkey);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_CURVE25519
|
||||
case SSH_KEX_CURVE25519_SHA256:
|
||||
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
|
||||
rc = ssh_buffer_pack(buf,
|
||||
"dPdP",
|
||||
CURVE25519_PUBKEY_SIZE,
|
||||
(size_t)CURVE25519_PUBKEY_SIZE, session->next_crypto->curve25519_client_pubkey,
|
||||
CURVE25519_PUBKEY_SIZE,
|
||||
(size_t)CURVE25519_PUBKEY_SIZE, session->next_crypto->curve25519_server_pubkey);
|
||||
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
rc = ssh_buffer_pack(buf, "B", session->next_crypto->k);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_hexa("hash buffer", ssh_buffer_get(buf), ssh_buffer_get_len(buf));
|
||||
#endif
|
||||
|
||||
switch (session->next_crypto->kex_type) {
|
||||
case SSH_KEX_DH_GROUP1_SHA1:
|
||||
case SSH_KEX_DH_GROUP14_SHA1:
|
||||
session->next_crypto->digest_len = SHA_DIGEST_LENGTH;
|
||||
session->next_crypto->mac_type = SSH_MAC_SHA1;
|
||||
session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len);
|
||||
if (session->next_crypto->secret_hash == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
sha1(ssh_buffer_get(buf), ssh_buffer_get_len(buf),
|
||||
session->next_crypto->secret_hash);
|
||||
break;
|
||||
case SSH_KEX_ECDH_SHA2_NISTP256:
|
||||
case SSH_KEX_CURVE25519_SHA256:
|
||||
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
|
||||
session->next_crypto->digest_len = SHA256_DIGEST_LENGTH;
|
||||
session->next_crypto->mac_type = SSH_MAC_SHA256;
|
||||
session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len);
|
||||
if (session->next_crypto->secret_hash == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
sha256(ssh_buffer_get(buf), ssh_buffer_get_len(buf),
|
||||
session->next_crypto->secret_hash);
|
||||
break;
|
||||
case SSH_KEX_ECDH_SHA2_NISTP384:
|
||||
session->next_crypto->digest_len = SHA384_DIGEST_LENGTH;
|
||||
session->next_crypto->mac_type = SSH_MAC_SHA384;
|
||||
session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len);
|
||||
if (session->next_crypto->secret_hash == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
sha384(ssh_buffer_get(buf), ssh_buffer_get_len(buf),
|
||||
session->next_crypto->secret_hash);
|
||||
break;
|
||||
case SSH_KEX_DH_GROUP16_SHA512:
|
||||
case SSH_KEX_DH_GROUP18_SHA512:
|
||||
case SSH_KEX_ECDH_SHA2_NISTP521:
|
||||
session->next_crypto->digest_len = SHA512_DIGEST_LENGTH;
|
||||
session->next_crypto->mac_type = SSH_MAC_SHA512;
|
||||
session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len);
|
||||
if (session->next_crypto->secret_hash == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
sha512(ssh_buffer_get(buf),
|
||||
ssh_buffer_get_len(buf),
|
||||
session->next_crypto->secret_hash);
|
||||
break;
|
||||
}
|
||||
/* During the first kex, secret hash and session ID are equal. However, after
|
||||
* a key re-exchange, a new secret hash is calculated. This hash will not replace
|
||||
* but complement existing session id.
|
||||
*/
|
||||
if (!session->next_crypto->session_id) {
|
||||
session->next_crypto->session_id = malloc(session->next_crypto->digest_len);
|
||||
if (session->next_crypto->session_id == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
memcpy(session->next_crypto->session_id, session->next_crypto->secret_hash,
|
||||
session->next_crypto->digest_len);
|
||||
}
|
||||
#ifdef DEBUG_CRYPTO
|
||||
printf("Session hash: \n");
|
||||
ssh_print_hexa("secret hash", session->next_crypto->secret_hash, session->next_crypto->digest_len);
|
||||
ssh_print_hexa("session id", session->next_crypto->session_id, session->next_crypto->digest_len);
|
||||
#endif
|
||||
|
||||
rc = SSH_OK;
|
||||
error:
|
||||
ssh_buffer_free(buf);
|
||||
ssh_buffer_free(client_hash);
|
||||
ssh_buffer_free(server_hash);
|
||||
|
||||
session->in_hashbuf = NULL;
|
||||
session->out_hashbuf = NULL;
|
||||
|
||||
ssh_string_free(num);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ssh_hashbufout_add_cookie(ssh_session session) {
|
||||
int rc;
|
||||
|
||||
session->out_hashbuf = ssh_buffer_new();
|
||||
if (session->out_hashbuf == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_allocate_size(session->out_hashbuf,
|
||||
sizeof(uint8_t) + 16);
|
||||
if (rc < 0) {
|
||||
ssh_buffer_reinit(session->out_hashbuf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ssh_buffer_add_u8(session->out_hashbuf, 20) < 0) {
|
||||
ssh_buffer_reinit(session->out_hashbuf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (session->server) {
|
||||
if (ssh_buffer_add_data(session->out_hashbuf,
|
||||
session->next_crypto->server_kex.cookie, 16) < 0) {
|
||||
ssh_buffer_reinit(session->out_hashbuf);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (ssh_buffer_add_data(session->out_hashbuf,
|
||||
session->next_crypto->client_kex.cookie, 16) < 0) {
|
||||
ssh_buffer_reinit(session->out_hashbuf);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ssh_hashbufin_add_cookie(ssh_session session, unsigned char *cookie) {
|
||||
int rc;
|
||||
|
||||
session->in_hashbuf = ssh_buffer_new();
|
||||
if (session->in_hashbuf == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_allocate_size(session->in_hashbuf,
|
||||
sizeof(uint8_t) + 20 + 16);
|
||||
if (rc < 0) {
|
||||
ssh_buffer_reinit(session->in_hashbuf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ssh_buffer_add_u8(session->in_hashbuf, 20) < 0) {
|
||||
ssh_buffer_reinit(session->in_hashbuf);
|
||||
return -1;
|
||||
}
|
||||
if (ssh_buffer_add_data(session->in_hashbuf,cookie, 16) < 0) {
|
||||
ssh_buffer_reinit(session->in_hashbuf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generate_one_key(ssh_string k,
|
||||
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) {
|
||||
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, &letter, 1);
|
||||
ssh_mac_update(ctx, crypto->session_id, crypto->digest_len);
|
||||
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 ssh_generate_session_keys(ssh_session session) {
|
||||
ssh_string k_string = NULL;
|
||||
struct ssh_crypto_struct *crypto = session->next_crypto;
|
||||
int rc = -1;
|
||||
|
||||
k_string = ssh_make_bignum_string(crypto->k);
|
||||
if (k_string == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
|
||||
crypto->encryptIV = malloc(crypto->digest_len);
|
||||
crypto->decryptIV = malloc(crypto->digest_len);
|
||||
crypto->encryptkey = malloc(crypto->digest_len);
|
||||
crypto->decryptkey = malloc(crypto->digest_len);
|
||||
crypto->encryptMAC = malloc(crypto->digest_len);
|
||||
crypto->decryptMAC = malloc(crypto->digest_len);
|
||||
if(crypto->encryptIV == NULL || crypto->decryptIV == NULL ||
|
||||
crypto->encryptkey == NULL || crypto->decryptkey == NULL ||
|
||||
crypto->encryptMAC == NULL || crypto->decryptMAC == NULL){
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* IV */
|
||||
if (session->client) {
|
||||
rc = generate_one_key(k_string, crypto, &crypto->encryptIV, 'A', crypto->digest_len);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
rc = generate_one_key(k_string, crypto, &crypto->decryptIV, 'B', crypto->digest_len);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
rc = generate_one_key(k_string, crypto, &crypto->decryptIV, 'A', crypto->digest_len);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
rc = generate_one_key(k_string, crypto, &crypto->encryptIV, 'B', crypto->digest_len);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (session->client) {
|
||||
rc = generate_one_key(k_string, crypto, &crypto->encryptkey, 'C', crypto->out_cipher->keysize / 8);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
rc = generate_one_key(k_string, crypto, &crypto->decryptkey, 'D', crypto->in_cipher->keysize / 8);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
rc = generate_one_key(k_string, crypto, &crypto->decryptkey, 'C', crypto->in_cipher->keysize / 8);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
rc = generate_one_key(k_string, crypto, &crypto->encryptkey, 'D', crypto->out_cipher->keysize / 8);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if(session->client) {
|
||||
rc = generate_one_key(k_string, crypto, &crypto->encryptMAC, 'E', hmac_digest_len(crypto->out_hmac));
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
rc = generate_one_key(k_string, crypto, &crypto->decryptMAC, 'F', hmac_digest_len(crypto->in_hmac));
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
rc = generate_one_key(k_string, crypto, &crypto->decryptMAC, 'E', hmac_digest_len(crypto->in_hmac));
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
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, 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;
|
||||
error:
|
||||
ssh_string_free(k_string);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @addtogroup libssh_session
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @deprecated Use ssh_get_publickey_hash()
|
||||
*/
|
||||
int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash) {
|
||||
ssh_key pubkey = NULL;
|
||||
ssh_string pubkey_blob = NULL;
|
||||
MD5CTX ctx;
|
||||
unsigned char *h;
|
||||
int rc;
|
||||
|
||||
if (session == NULL || hash == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
*hash = NULL;
|
||||
if (session->current_crypto == NULL ||
|
||||
session->current_crypto->server_pubkey == NULL) {
|
||||
ssh_set_error(session,SSH_FATAL,"No current cryptographic context");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
h = calloc(MD5_DIGEST_LEN, sizeof(unsigned char));
|
||||
if (h == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
ctx = md5_init();
|
||||
if (ctx == NULL) {
|
||||
SAFE_FREE(h);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = ssh_get_server_publickey(session, &pubkey);
|
||||
if (rc != SSH_OK) {
|
||||
md5_final(h, ctx);
|
||||
SAFE_FREE(h);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = ssh_pki_export_pubkey_blob(pubkey, &pubkey_blob);
|
||||
ssh_key_free(pubkey);
|
||||
if (rc != SSH_OK) {
|
||||
md5_final(h, ctx);
|
||||
SAFE_FREE(h);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
md5_update(ctx, ssh_string_data(pubkey_blob), ssh_string_len(pubkey_blob));
|
||||
ssh_string_free(pubkey_blob);
|
||||
md5_final(h, ctx);
|
||||
|
||||
*hash = h;
|
||||
|
||||
return MD5_DIGEST_LEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deallocate the hash obtained by ssh_get_pubkey_hash.
|
||||
*
|
||||
* This is required under Microsoft platform as this library might use a
|
||||
* different C library than your software, hence a different heap.
|
||||
*
|
||||
* @param[in] hash The buffer to deallocate.
|
||||
*
|
||||
* @see ssh_get_pubkey_hash()
|
||||
*/
|
||||
void ssh_clean_pubkey_hash(unsigned char **hash) {
|
||||
SAFE_FREE(*hash);
|
||||
*hash = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the server public key from a session.
|
||||
*
|
||||
* @param[in] session The session to get the key from.
|
||||
*
|
||||
* @param[out] key A pointer to store the allocated key. You need to free
|
||||
* the key.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR on errror.
|
||||
*
|
||||
* @see ssh_key_free()
|
||||
*/
|
||||
int ssh_get_server_publickey(ssh_session session, ssh_key *key)
|
||||
{
|
||||
ssh_key pubkey = NULL;
|
||||
|
||||
if (session == NULL ||
|
||||
session->current_crypto == NULL ||
|
||||
session->current_crypto->server_pubkey == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
pubkey = ssh_key_dup(session->current_crypto->server_pubkey);
|
||||
if (pubkey == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
*key = pubkey;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
ssh_key ssh_dh_get_current_server_publickey(ssh_session session)
|
||||
{
|
||||
@ -1479,138 +960,6 @@ int ssh_dh_get_next_server_publickey_blob(ssh_session session,
|
||||
return ssh_pki_export_pubkey_blob(pubkey, pubkey_blob);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use ssh_get_server_publickey()
|
||||
*/
|
||||
int ssh_get_publickey(ssh_session session, ssh_key *key)
|
||||
{
|
||||
return ssh_get_server_publickey(session, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocates a buffer with the hash of the public key.
|
||||
*
|
||||
* This function allows you to get a hash of the public key. You can then
|
||||
* print this hash in a human-readable form to the user so that he is able to
|
||||
* verify it. Use ssh_get_hexa() or ssh_print_hexa() to display it.
|
||||
*
|
||||
* @param[in] key The public key to create the hash for.
|
||||
*
|
||||
* @param[in] type The type of the hash you want.
|
||||
*
|
||||
* @param[in] hash A pointer to store the allocated buffer. It can be
|
||||
* freed using ssh_clean_pubkey_hash().
|
||||
*
|
||||
* @param[in] hlen The length of the hash.
|
||||
*
|
||||
* @return 0 on success, -1 if an error occured.
|
||||
*
|
||||
* @warning It is very important that you verify at some moment that the hash
|
||||
* matches a known server. If you don't do it, cryptography wont help
|
||||
* you at making things secure.
|
||||
* OpenSSH uses SHA1 to print public key digests.
|
||||
*
|
||||
* @see ssh_session_update_known_hosts()
|
||||
* @see ssh_get_hexa()
|
||||
* @see ssh_print_hexa()
|
||||
* @see ssh_clean_pubkey_hash()
|
||||
*/
|
||||
int ssh_get_publickey_hash(const ssh_key key,
|
||||
enum ssh_publickey_hash_type type,
|
||||
unsigned char **hash,
|
||||
size_t *hlen)
|
||||
{
|
||||
ssh_string blob;
|
||||
unsigned char *h;
|
||||
int rc;
|
||||
|
||||
rc = ssh_pki_export_pubkey_blob(key, &blob);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SSH_PUBLICKEY_HASH_SHA1:
|
||||
{
|
||||
SHACTX ctx;
|
||||
|
||||
h = malloc(SHA_DIGEST_LEN);
|
||||
if (h == NULL) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx = sha1_init();
|
||||
if (ctx == NULL) {
|
||||
free(h);
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sha1_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
|
||||
sha1_final(h, ctx);
|
||||
|
||||
*hlen = SHA_DIGEST_LEN;
|
||||
}
|
||||
break;
|
||||
case SSH_PUBLICKEY_HASH_SHA256:
|
||||
{
|
||||
SHA256CTX ctx;
|
||||
|
||||
h = malloc(SHA256_DIGEST_LEN);
|
||||
if (h == NULL) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx = sha256_init();
|
||||
if (ctx == NULL) {
|
||||
free(h);
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sha256_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
|
||||
sha256_final(h, ctx);
|
||||
|
||||
*hlen = SHA256_DIGEST_LEN;
|
||||
}
|
||||
break;
|
||||
case SSH_PUBLICKEY_HASH_MD5:
|
||||
{
|
||||
MD5CTX ctx;
|
||||
|
||||
h = malloc(MD5_DIGEST_LEN);
|
||||
if (h == NULL) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx = md5_init();
|
||||
if (ctx == NULL) {
|
||||
free(h);
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
md5_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
|
||||
md5_final(h, ctx);
|
||||
|
||||
*hlen = MD5_DIGEST_LEN;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*hash = h;
|
||||
rc = 0;
|
||||
out:
|
||||
ssh_string_free(blob);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
@ -1643,43 +992,6 @@ static char *ssh_get_b64_unpadded(const unsigned char *hash, size_t len)
|
||||
return b64_unpadded;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a buffer into a colon separated hex string.
|
||||
* The caller has to free the memory.
|
||||
*
|
||||
* @param what What should be converted to a hex string.
|
||||
*
|
||||
* @param len Length of the buffer to convert.
|
||||
*
|
||||
* @return The hex string or NULL on error.
|
||||
*
|
||||
* @see ssh_string_free_char()
|
||||
*/
|
||||
char *ssh_get_hexa(const unsigned char *what, size_t len) {
|
||||
const char h[] = "0123456789abcdef";
|
||||
char *hexa;
|
||||
size_t i;
|
||||
size_t hlen = len * 3;
|
||||
|
||||
if (len > (UINT_MAX - 1) / 3) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hexa = malloc(hlen + 1);
|
||||
if (hexa == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
hexa[i * 3] = h[(what[i] >> 4) & 0xF];
|
||||
hexa[i * 3 + 1] = h[what[i] & 0xF];
|
||||
hexa[i * 3 + 2] = ':';
|
||||
}
|
||||
hexa[hlen - 1] = '\0';
|
||||
|
||||
return hexa;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a hash as a human-readable hex- or base64-string.
|
||||
*
|
||||
@ -1784,24 +1096,4 @@ void ssh_print_hash(enum ssh_publickey_hash_type type,
|
||||
SAFE_FREE(fingerprint);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Print a buffer as colon separated hex string.
|
||||
*
|
||||
* @param descr Description printed in front of the hex string.
|
||||
*
|
||||
* @param what What should be converted to a hex string.
|
||||
*
|
||||
* @param len Length of the buffer to convert.
|
||||
*/
|
||||
void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len) {
|
||||
char *hexa = ssh_get_hexa(what, len);
|
||||
|
||||
if (hexa == NULL) {
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "%s: %s\n", descr, hexa);
|
||||
|
||||
free(hexa);
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
490
src/kex.c
490
src/kex.c
@ -39,6 +39,7 @@
|
||||
#include "libssh/knownhosts.h"
|
||||
#include "libssh/misc.h"
|
||||
#include "libssh/pki.h"
|
||||
#include "libssh/bignum.h"
|
||||
|
||||
#ifdef WITH_BLOWFISH_CIPHER
|
||||
# if defined(HAVE_OPENSSL_BLOWFISH_H) || defined(HAVE_LIBGCRYPT) || defined(HAVE_LIBMBEDCRYPTO)
|
||||
@ -989,3 +990,492 @@ char *ssh_keep_known_algos(enum ssh_kex_types_e algo, const char *list)
|
||||
|
||||
return ssh_find_all_matching(supported_methods[algo], list);
|
||||
}
|
||||
|
||||
int ssh_make_sessionid(ssh_session session)
|
||||
{
|
||||
ssh_string num = NULL;
|
||||
ssh_buffer server_hash = NULL;
|
||||
ssh_buffer client_hash = NULL;
|
||||
ssh_buffer buf = NULL;
|
||||
ssh_string server_pubkey_blob = NULL;
|
||||
int rc = SSH_ERROR;
|
||||
|
||||
buf = ssh_buffer_new();
|
||||
if (buf == NULL) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_pack(buf,
|
||||
"ss",
|
||||
session->clientbanner,
|
||||
session->serverbanner);
|
||||
if (rc == SSH_ERROR) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (session->client) {
|
||||
server_hash = session->in_hashbuf;
|
||||
client_hash = session->out_hashbuf;
|
||||
} else {
|
||||
server_hash = session->out_hashbuf;
|
||||
client_hash = session->in_hashbuf;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle the two final fields for the KEXINIT message (RFC 4253 7.1):
|
||||
*
|
||||
* boolean first_kex_packet_follows
|
||||
* uint32 0 (reserved for future extension)
|
||||
*/
|
||||
rc = ssh_buffer_add_u8(server_hash, 0);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
rc = ssh_buffer_add_u32(server_hash, 0);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* These fields are handled for the server case in ssh_packet_kexinit. */
|
||||
if (session->client) {
|
||||
rc = ssh_buffer_add_u8(client_hash, 0);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
rc = ssh_buffer_add_u32(client_hash, 0);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
rc = ssh_dh_get_next_server_publickey_blob(session, &server_pubkey_blob);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_pack(buf,
|
||||
"dPdPS",
|
||||
ssh_buffer_get_len(client_hash),
|
||||
ssh_buffer_get_len(client_hash),
|
||||
ssh_buffer_get(client_hash),
|
||||
ssh_buffer_get_len(server_hash),
|
||||
ssh_buffer_get_len(server_hash),
|
||||
ssh_buffer_get(server_hash),
|
||||
server_pubkey_blob);
|
||||
ssh_string_free(server_pubkey_blob);
|
||||
if(rc != SSH_OK){
|
||||
goto error;
|
||||
}
|
||||
|
||||
switch(session->next_crypto->kex_type) {
|
||||
case SSH_KEX_DH_GROUP1_SHA1:
|
||||
case SSH_KEX_DH_GROUP14_SHA1:
|
||||
case SSH_KEX_DH_GROUP16_SHA512:
|
||||
case SSH_KEX_DH_GROUP18_SHA512:
|
||||
rc = ssh_buffer_pack(buf,
|
||||
"BB",
|
||||
session->next_crypto->e,
|
||||
session->next_crypto->f);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
#ifdef HAVE_ECDH
|
||||
case SSH_KEX_ECDH_SHA2_NISTP256:
|
||||
case SSH_KEX_ECDH_SHA2_NISTP384:
|
||||
case SSH_KEX_ECDH_SHA2_NISTP521:
|
||||
if (session->next_crypto->ecdh_client_pubkey == NULL ||
|
||||
session->next_crypto->ecdh_server_pubkey == NULL) {
|
||||
SSH_LOG(SSH_LOG_WARNING, "ECDH parameted missing");
|
||||
goto error;
|
||||
}
|
||||
rc = ssh_buffer_pack(buf,
|
||||
"SS",
|
||||
session->next_crypto->ecdh_client_pubkey,
|
||||
session->next_crypto->ecdh_server_pubkey);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_CURVE25519
|
||||
case SSH_KEX_CURVE25519_SHA256:
|
||||
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
|
||||
rc = ssh_buffer_pack(buf,
|
||||
"dPdP",
|
||||
CURVE25519_PUBKEY_SIZE,
|
||||
(size_t)CURVE25519_PUBKEY_SIZE, session->next_crypto->curve25519_client_pubkey,
|
||||
CURVE25519_PUBKEY_SIZE,
|
||||
(size_t)CURVE25519_PUBKEY_SIZE, session->next_crypto->curve25519_server_pubkey);
|
||||
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
rc = ssh_buffer_pack(buf, "B", session->next_crypto->k);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_CRYPTO
|
||||
ssh_print_hexa("hash buffer", ssh_buffer_get(buf), ssh_buffer_get_len(buf));
|
||||
#endif
|
||||
|
||||
switch (session->next_crypto->kex_type) {
|
||||
case SSH_KEX_DH_GROUP1_SHA1:
|
||||
case SSH_KEX_DH_GROUP14_SHA1:
|
||||
session->next_crypto->digest_len = SHA_DIGEST_LENGTH;
|
||||
session->next_crypto->mac_type = SSH_MAC_SHA1;
|
||||
session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len);
|
||||
if (session->next_crypto->secret_hash == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
sha1(ssh_buffer_get(buf), ssh_buffer_get_len(buf),
|
||||
session->next_crypto->secret_hash);
|
||||
break;
|
||||
case SSH_KEX_ECDH_SHA2_NISTP256:
|
||||
case SSH_KEX_CURVE25519_SHA256:
|
||||
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
|
||||
session->next_crypto->digest_len = SHA256_DIGEST_LENGTH;
|
||||
session->next_crypto->mac_type = SSH_MAC_SHA256;
|
||||
session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len);
|
||||
if (session->next_crypto->secret_hash == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
sha256(ssh_buffer_get(buf), ssh_buffer_get_len(buf),
|
||||
session->next_crypto->secret_hash);
|
||||
break;
|
||||
case SSH_KEX_ECDH_SHA2_NISTP384:
|
||||
session->next_crypto->digest_len = SHA384_DIGEST_LENGTH;
|
||||
session->next_crypto->mac_type = SSH_MAC_SHA384;
|
||||
session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len);
|
||||
if (session->next_crypto->secret_hash == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
sha384(ssh_buffer_get(buf), ssh_buffer_get_len(buf),
|
||||
session->next_crypto->secret_hash);
|
||||
break;
|
||||
case SSH_KEX_DH_GROUP16_SHA512:
|
||||
case SSH_KEX_DH_GROUP18_SHA512:
|
||||
case SSH_KEX_ECDH_SHA2_NISTP521:
|
||||
session->next_crypto->digest_len = SHA512_DIGEST_LENGTH;
|
||||
session->next_crypto->mac_type = SSH_MAC_SHA512;
|
||||
session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len);
|
||||
if (session->next_crypto->secret_hash == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
sha512(ssh_buffer_get(buf),
|
||||
ssh_buffer_get_len(buf),
|
||||
session->next_crypto->secret_hash);
|
||||
break;
|
||||
}
|
||||
/* During the first kex, secret hash and session ID are equal. However, after
|
||||
* a key re-exchange, a new secret hash is calculated. This hash will not replace
|
||||
* but complement existing session id.
|
||||
*/
|
||||
if (!session->next_crypto->session_id) {
|
||||
session->next_crypto->session_id = malloc(session->next_crypto->digest_len);
|
||||
if (session->next_crypto->session_id == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
memcpy(session->next_crypto->session_id, session->next_crypto->secret_hash,
|
||||
session->next_crypto->digest_len);
|
||||
}
|
||||
#ifdef DEBUG_CRYPTO
|
||||
printf("Session hash: \n");
|
||||
ssh_print_hexa("secret hash", session->next_crypto->secret_hash, session->next_crypto->digest_len);
|
||||
ssh_print_hexa("session id", session->next_crypto->session_id, session->next_crypto->digest_len);
|
||||
#endif
|
||||
|
||||
rc = SSH_OK;
|
||||
error:
|
||||
ssh_buffer_free(buf);
|
||||
ssh_buffer_free(client_hash);
|
||||
ssh_buffer_free(server_hash);
|
||||
|
||||
session->in_hashbuf = NULL;
|
||||
session->out_hashbuf = NULL;
|
||||
|
||||
ssh_string_free(num);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int ssh_hashbufout_add_cookie(ssh_session session)
|
||||
{
|
||||
int rc;
|
||||
|
||||
session->out_hashbuf = ssh_buffer_new();
|
||||
if (session->out_hashbuf == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_allocate_size(session->out_hashbuf,
|
||||
sizeof(uint8_t) + 16);
|
||||
if (rc < 0) {
|
||||
ssh_buffer_reinit(session->out_hashbuf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ssh_buffer_add_u8(session->out_hashbuf, 20) < 0) {
|
||||
ssh_buffer_reinit(session->out_hashbuf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (session->server) {
|
||||
if (ssh_buffer_add_data(session->out_hashbuf,
|
||||
session->next_crypto->server_kex.cookie, 16) < 0) {
|
||||
ssh_buffer_reinit(session->out_hashbuf);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (ssh_buffer_add_data(session->out_hashbuf,
|
||||
session->next_crypto->client_kex.cookie, 16) < 0) {
|
||||
ssh_buffer_reinit(session->out_hashbuf);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ssh_hashbufin_add_cookie(ssh_session session, unsigned char *cookie)
|
||||
{
|
||||
int rc;
|
||||
|
||||
session->in_hashbuf = ssh_buffer_new();
|
||||
if (session->in_hashbuf == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = ssh_buffer_allocate_size(session->in_hashbuf,
|
||||
sizeof(uint8_t) + 20 + 16);
|
||||
if (rc < 0) {
|
||||
ssh_buffer_reinit(session->in_hashbuf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ssh_buffer_add_u8(session->in_hashbuf, 20) < 0) {
|
||||
ssh_buffer_reinit(session->in_hashbuf);
|
||||
return -1;
|
||||
}
|
||||
if (ssh_buffer_add_data(session->in_hashbuf,cookie, 16) < 0) {
|
||||
ssh_buffer_reinit(session->in_hashbuf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int generate_one_key(ssh_string k,
|
||||
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) {
|
||||
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, &letter, 1);
|
||||
ssh_mac_update(ctx, crypto->session_id, crypto->digest_len);
|
||||
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 ssh_generate_session_keys(ssh_session session)
|
||||
{
|
||||
ssh_string k_string = NULL;
|
||||
struct ssh_crypto_struct *crypto = session->next_crypto;
|
||||
int rc = -1;
|
||||
|
||||
k_string = ssh_make_bignum_string(crypto->k);
|
||||
if (k_string == NULL) {
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
|
||||
crypto->encryptIV = malloc(crypto->digest_len);
|
||||
crypto->decryptIV = malloc(crypto->digest_len);
|
||||
crypto->encryptkey = malloc(crypto->digest_len);
|
||||
crypto->decryptkey = malloc(crypto->digest_len);
|
||||
crypto->encryptMAC = malloc(crypto->digest_len);
|
||||
crypto->decryptMAC = malloc(crypto->digest_len);
|
||||
if (crypto->encryptIV == NULL ||
|
||||
crypto->decryptIV == NULL ||
|
||||
crypto->encryptkey == NULL || crypto->decryptkey == NULL ||
|
||||
crypto->encryptMAC == NULL || crypto->decryptMAC == NULL){
|
||||
ssh_set_error_oom(session);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* IV */
|
||||
if (session->client) {
|
||||
rc = generate_one_key(k_string,
|
||||
crypto,
|
||||
&crypto->encryptIV,
|
||||
'A',
|
||||
crypto->digest_len);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
rc = generate_one_key(k_string,
|
||||
crypto,
|
||||
&crypto->decryptIV,
|
||||
'B',
|
||||
crypto->digest_len);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
rc = generate_one_key(k_string,
|
||||
crypto,
|
||||
&crypto->decryptIV,
|
||||
'A',
|
||||
crypto->digest_len);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
rc = generate_one_key(k_string,
|
||||
crypto,
|
||||
&crypto->encryptIV,
|
||||
'B',
|
||||
crypto->digest_len);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (session->client) {
|
||||
rc = generate_one_key(k_string,
|
||||
crypto,
|
||||
&crypto->encryptkey,
|
||||
'C',
|
||||
crypto->out_cipher->keysize / 8);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
rc = generate_one_key(k_string,
|
||||
crypto,
|
||||
&crypto->decryptkey,
|
||||
'D',
|
||||
crypto->in_cipher->keysize / 8);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
rc = generate_one_key(k_string,
|
||||
crypto,
|
||||
&crypto->decryptkey,
|
||||
'C',
|
||||
crypto->in_cipher->keysize / 8);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
rc = generate_one_key(k_string,
|
||||
crypto,
|
||||
&crypto->encryptkey,
|
||||
'D',
|
||||
crypto->out_cipher->keysize / 8);
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if(session->client) {
|
||||
rc = generate_one_key(k_string,
|
||||
crypto,
|
||||
&crypto->encryptMAC,
|
||||
'E',
|
||||
hmac_digest_len(crypto->out_hmac));
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
rc = generate_one_key(k_string,
|
||||
crypto,
|
||||
&crypto->decryptMAC,
|
||||
'F',
|
||||
hmac_digest_len(crypto->in_hmac));
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
rc = generate_one_key(k_string,
|
||||
crypto,
|
||||
&crypto->decryptMAC,
|
||||
'E',
|
||||
hmac_digest_len(crypto->in_hmac));
|
||||
if (rc < 0) {
|
||||
goto error;
|
||||
}
|
||||
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,
|
||||
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;
|
||||
error:
|
||||
ssh_string_free(k_string);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
57
src/misc.c
57
src/misc.c
@ -332,6 +332,63 @@ char *ssh_hostport(const char *host, int port){
|
||||
return dest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a buffer into a colon separated hex string.
|
||||
* The caller has to free the memory.
|
||||
*
|
||||
* @param what What should be converted to a hex string.
|
||||
*
|
||||
* @param len Length of the buffer to convert.
|
||||
*
|
||||
* @return The hex string or NULL on error.
|
||||
*
|
||||
* @see ssh_string_free_char()
|
||||
*/
|
||||
char *ssh_get_hexa(const unsigned char *what, size_t len) {
|
||||
const char h[] = "0123456789abcdef";
|
||||
char *hexa;
|
||||
size_t i;
|
||||
size_t hlen = len * 3;
|
||||
|
||||
if (len > (UINT_MAX - 1) / 3) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hexa = malloc(hlen + 1);
|
||||
if (hexa == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
hexa[i * 3] = h[(what[i] >> 4) & 0xF];
|
||||
hexa[i * 3 + 1] = h[what[i] & 0xF];
|
||||
hexa[i * 3 + 2] = ':';
|
||||
}
|
||||
hexa[hlen - 1] = '\0';
|
||||
|
||||
return hexa;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Print a buffer as colon separated hex string.
|
||||
*
|
||||
* @param descr Description printed in front of the hex string.
|
||||
*
|
||||
* @param what What should be converted to a hex string.
|
||||
*
|
||||
* @param len Length of the buffer to convert.
|
||||
*/
|
||||
void ssh_print_hexa(const char *descr, const unsigned char *what, size_t len) {
|
||||
char *hexa = ssh_get_hexa(what, len);
|
||||
|
||||
if (hexa == NULL) {
|
||||
return;
|
||||
}
|
||||
fprintf(stderr, "%s: %s\n", descr, hexa);
|
||||
|
||||
free(hexa);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if libssh is the required version or get the version
|
||||
* string.
|
||||
|
234
src/session.c
234
src/session.c
@ -38,6 +38,7 @@
|
||||
#include "libssh/misc.h"
|
||||
#include "libssh/buffer.h"
|
||||
#include "libssh/poll.h"
|
||||
#include "libssh/pki.h"
|
||||
|
||||
#define FIRST_CHANNEL 42 // why not ? it helps to find bugs.
|
||||
|
||||
@ -949,4 +950,237 @@ void ssh_set_counters(ssh_session session, ssh_counter scounter,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use ssh_get_publickey_hash()
|
||||
*/
|
||||
int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash)
|
||||
{
|
||||
ssh_key pubkey = NULL;
|
||||
ssh_string pubkey_blob = NULL;
|
||||
MD5CTX ctx;
|
||||
unsigned char *h;
|
||||
int rc;
|
||||
|
||||
if (session == NULL || hash == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
*hash = NULL;
|
||||
if (session->current_crypto == NULL ||
|
||||
session->current_crypto->server_pubkey == NULL) {
|
||||
ssh_set_error(session,SSH_FATAL,"No current cryptographic context");
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
h = calloc(MD5_DIGEST_LEN, sizeof(unsigned char));
|
||||
if (h == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
ctx = md5_init();
|
||||
if (ctx == NULL) {
|
||||
SAFE_FREE(h);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = ssh_get_server_publickey(session, &pubkey);
|
||||
if (rc != SSH_OK) {
|
||||
md5_final(h, ctx);
|
||||
SAFE_FREE(h);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = ssh_pki_export_pubkey_blob(pubkey, &pubkey_blob);
|
||||
ssh_key_free(pubkey);
|
||||
if (rc != SSH_OK) {
|
||||
md5_final(h, ctx);
|
||||
SAFE_FREE(h);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
md5_update(ctx, ssh_string_data(pubkey_blob), ssh_string_len(pubkey_blob));
|
||||
ssh_string_free(pubkey_blob);
|
||||
md5_final(h, ctx);
|
||||
|
||||
*hash = h;
|
||||
|
||||
return MD5_DIGEST_LEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Deallocate the hash obtained by ssh_get_pubkey_hash.
|
||||
*
|
||||
* This is required under Microsoft platform as this library might use a
|
||||
* different C library than your software, hence a different heap.
|
||||
*
|
||||
* @param[in] hash The buffer to deallocate.
|
||||
*
|
||||
* @see ssh_get_pubkey_hash()
|
||||
*/
|
||||
void ssh_clean_pubkey_hash(unsigned char **hash) {
|
||||
SAFE_FREE(*hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the server public key from a session.
|
||||
*
|
||||
* @param[in] session The session to get the key from.
|
||||
*
|
||||
* @param[out] key A pointer to store the allocated key. You need to free
|
||||
* the key.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR on errror.
|
||||
*
|
||||
* @see ssh_key_free()
|
||||
*/
|
||||
int ssh_get_server_publickey(ssh_session session, ssh_key *key)
|
||||
{
|
||||
ssh_key pubkey = NULL;
|
||||
|
||||
if (session == NULL ||
|
||||
session->current_crypto == NULL ||
|
||||
session->current_crypto->server_pubkey == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
pubkey = ssh_key_dup(session->current_crypto->server_pubkey);
|
||||
if (pubkey == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
*key = pubkey;
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use ssh_get_server_publickey()
|
||||
*/
|
||||
int ssh_get_publickey(ssh_session session, ssh_key *key)
|
||||
{
|
||||
return ssh_get_server_publickey(session, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Allocates a buffer with the hash of the public key.
|
||||
*
|
||||
* This function allows you to get a hash of the public key. You can then
|
||||
* print this hash in a human-readable form to the user so that he is able to
|
||||
* verify it. Use ssh_get_hexa() or ssh_print_hexa() to display it.
|
||||
*
|
||||
* @param[in] key The public key to create the hash for.
|
||||
*
|
||||
* @param[in] type The type of the hash you want.
|
||||
*
|
||||
* @param[in] hash A pointer to store the allocated buffer. It can be
|
||||
* freed using ssh_clean_pubkey_hash().
|
||||
*
|
||||
* @param[in] hlen The length of the hash.
|
||||
*
|
||||
* @return 0 on success, -1 if an error occured.
|
||||
*
|
||||
* @warning It is very important that you verify at some moment that the hash
|
||||
* matches a known server. If you don't do it, cryptography wont help
|
||||
* you at making things secure.
|
||||
* OpenSSH uses SHA1 to print public key digests.
|
||||
*
|
||||
* @see ssh_session_update_known_hosts()
|
||||
* @see ssh_get_hexa()
|
||||
* @see ssh_print_hexa()
|
||||
* @see ssh_clean_pubkey_hash()
|
||||
*/
|
||||
int ssh_get_publickey_hash(const ssh_key key,
|
||||
enum ssh_publickey_hash_type type,
|
||||
unsigned char **hash,
|
||||
size_t *hlen)
|
||||
{
|
||||
ssh_string blob;
|
||||
unsigned char *h;
|
||||
int rc;
|
||||
|
||||
rc = ssh_pki_export_pubkey_blob(key, &blob);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case SSH_PUBLICKEY_HASH_SHA1:
|
||||
{
|
||||
SHACTX ctx;
|
||||
|
||||
h = malloc(SHA_DIGEST_LEN);
|
||||
if (h == NULL) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx = sha1_init();
|
||||
if (ctx == NULL) {
|
||||
free(h);
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sha1_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
|
||||
sha1_final(h, ctx);
|
||||
|
||||
*hlen = SHA_DIGEST_LEN;
|
||||
}
|
||||
break;
|
||||
case SSH_PUBLICKEY_HASH_SHA256:
|
||||
{
|
||||
SHA256CTX ctx;
|
||||
|
||||
h = malloc(SHA256_DIGEST_LEN);
|
||||
if (h == NULL) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx = sha256_init();
|
||||
if (ctx == NULL) {
|
||||
free(h);
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sha256_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
|
||||
sha256_final(h, ctx);
|
||||
|
||||
*hlen = SHA256_DIGEST_LEN;
|
||||
}
|
||||
break;
|
||||
case SSH_PUBLICKEY_HASH_MD5:
|
||||
{
|
||||
MD5CTX ctx;
|
||||
|
||||
h = malloc(MD5_DIGEST_LEN);
|
||||
if (h == NULL) {
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ctx = md5_init();
|
||||
if (ctx == NULL) {
|
||||
free(h);
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
md5_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
|
||||
md5_final(h, ctx);
|
||||
|
||||
*hlen = MD5_DIGEST_LEN;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
rc = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*hash = h;
|
||||
rc = 0;
|
||||
out:
|
||||
ssh_string_free(blob);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
Загрузка…
x
Ссылка в новой задаче
Block a user