aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/libssh/dh.h6
-rw-r--r--include/libssh/kex.h5
-rw-r--r--src/dh.c708
-rw-r--r--src/kex.c490
-rw-r--r--src/misc.c57
-rw-r--r--src/session.c234
6 files changed, 786 insertions, 714 deletions
diff --git a/include/libssh/dh.h b/include/libssh/dh.h
index 3d7312c5..adc8c48f 100644
--- a/include/libssh/dh.h
+++ b/include/libssh/dh.h
@@ -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 */
diff --git a/include/libssh/kex.h b/include/libssh/kex.h
index 644a3956..19a67c81 100644
--- a/include/libssh/kex.h
+++ b/include/libssh/kex.h
@@ -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_ */
diff --git a/src/dh.c b/src/dh.c
index 6158b465..3fb7e6c4 100644
--- a/src/dh.c
+++ b/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)
{
@@ -1480,138 +961,6 @@ int ssh_dh_get_next_server_publickey_blob(ssh_session session,
}
/**
- * @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
*
* @brief Convert a buffer into an unpadded base64 string.
@@ -1644,43 +993,6 @@ static char *ssh_get_b64_unpadded(const unsigned char *hash, size_t len)
}
/**
- * @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.
*
* This gets an allocated fingerprint hash. It is a hex strings if the given
@@ -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);
-}
-
/** @} */
diff --git a/src/kex.c b/src/kex.c
index 03c843e0..dff5f47b 100644
--- a/src/kex.c
+++ b/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;
+}
diff --git a/src/misc.c b/src/misc.c
index 782b7bc0..abb82425 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -333,6 +333,63 @@ char *ssh_hostport(const char *host, int port){
}
/**
+ * @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.
*
diff --git a/src/session.c b/src/session.c
index 01a96a32..b79f4548 100644
--- a/src/session.c
+++ b/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;
+}
+
/** @} */