aboutsummaryrefslogtreecommitdiff
path: root/src/kex.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kex.c')
-rw-r--r--src/kex.c490
1 files changed, 490 insertions, 0 deletions
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;
+}