diff options
-rw-r--r-- | config.h.cmake | 4 | ||||
-rw-r--r-- | include/libssh/crypto.h | 35 | ||||
-rw-r--r-- | include/libssh/dh.h | 3 | ||||
-rw-r--r-- | include/libssh/ecdh.h | 39 | ||||
-rw-r--r-- | include/libssh/libcrypto.h | 5 | ||||
-rw-r--r-- | include/libssh/libgcrypt.h | 7 | ||||
-rw-r--r-- | include/libssh/session.h | 2 | ||||
-rw-r--r-- | include/libssh/ssh2.h | 4 | ||||
-rw-r--r-- | include/libssh/wrapper.h | 24 | ||||
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/client.c | 83 | ||||
-rw-r--r-- | src/crypt.c | 4 | ||||
-rw-r--r-- | src/dh.c | 249 | ||||
-rw-r--r-- | src/ecdh.c | 167 | ||||
-rw-r--r-- | src/kex.c | 18 | ||||
-rw-r--r-- | src/known_hosts.c | 2 | ||||
-rw-r--r-- | src/libcrypto.c | 86 | ||||
-rw-r--r-- | src/libgcrypt.c | 63 |
18 files changed, 635 insertions, 161 deletions
diff --git a/config.h.cmake b/config.h.cmake index e0ed57c3..f08c3801 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -35,6 +35,10 @@ /* Define to 1 if you have the <openssl/des.h> header file. */ #cmakedefine HAVE_OPENSSL_DES_H 1 +/* Define to 1 if you have the <openssl/ecdh.h> header file. */ +#cmakedefine HAVE_OPENSSL_ECDH_H 1 + + /* Define to 1 if you have the <pthread.h> header file. */ #cmakedefine HAVE_PTHREAD_H 1 diff --git a/include/libssh/crypto.h b/include/libssh/crypto.h index 1c543f50..d5b95b9e 100644 --- a/include/libssh/crypto.h +++ b/include/libssh/crypto.h @@ -40,18 +40,31 @@ #undef cbc_decrypt #endif -struct ssh_crypto_struct { - bignum e,f,x,k,y; - unsigned char session_id[SHA_DIGEST_LEN]; - - unsigned char encryptIV[SHA_DIGEST_LEN*2]; - unsigned char decryptIV[SHA_DIGEST_LEN*2]; +#ifdef HAVE_OPENSSL_ECDH_H +#include <openssl/ecdh.h> +#endif - unsigned char decryptkey[SHA_DIGEST_LEN*2]; - unsigned char encryptkey[SHA_DIGEST_LEN*2]; +enum ssh_key_exchange_e { + /* diffie-hellman-group1-sha1 */ + SSH_KEX_DH_GROUP1_SHA1=1, + /* ecdh-sha2-nistp256 */ + SSH_KEX_ECDH_SHA2_NISTP256 +}; - unsigned char encryptMAC[SHA_DIGEST_LEN]; - unsigned char decryptMAC[SHA_DIGEST_LEN]; +struct ssh_crypto_struct { + bignum e,f,x,k,y; + EC_KEY *ecdh_privkey; + ssh_string ecdh_client_pubkey; + ssh_string ecdh_server_pubkey; + ssh_string dh_server_signature; /* information used by dh_handshake. */ + size_t digest_len; /* len of all the fields below */ + unsigned char *session_id; + unsigned char *encryptIV; + unsigned char *decryptIV; + unsigned char *decryptkey; + unsigned char *encryptkey; + unsigned char *encryptMAC; + unsigned char *decryptMAC; unsigned char hmacbuf[EVP_MAX_MD_SIZE]; struct crypto_struct *in_cipher, *out_cipher; /* the cipher structures/objects */ ssh_string server_pubkey; @@ -62,6 +75,8 @@ struct ssh_crypto_struct { int delayed_compress_out; void *compress_out_ctx; /* don't touch it */ void *compress_in_ctx; /* really, don't */ + enum ssh_key_exchange_e kex_type; + enum ssh_mac_e mac_type; /* Mac operations to use for key gen */ }; struct crypto_struct { diff --git a/include/libssh/dh.h b/include/libssh/dh.h index ece7c019..d4cd0c2e 100644 --- a/include/libssh/dh.h +++ b/include/libssh/dh.h @@ -41,6 +41,9 @@ int dh_import_f(ssh_session session,ssh_string f_string); int dh_import_e(ssh_session session, ssh_string e_string); void dh_import_pubkey(ssh_session session,ssh_string pubkey_string); int dh_build_k(ssh_session session); +int ssh_client_dh_init(ssh_session session); +int ssh_client_dh_reply(ssh_session session, ssh_buffer packet); + int make_sessionid(ssh_session session); /* add data for the final cookie */ int hashbufin_add_cookie(ssh_session session, unsigned char *cookie); diff --git a/include/libssh/ecdh.h b/include/libssh/ecdh.h new file mode 100644 index 00000000..5cc208ce --- /dev/null +++ b/include/libssh/ecdh.h @@ -0,0 +1,39 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2011 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#ifndef ECDH_H_ +#define ECDH_H_ + +#include "config.h" + +#ifdef HAVE_LIBCRYPTO +#ifdef HAVE_OPENSSL_ECDH_H + +#define HAVE_ECDH + +#endif /* HAVE_OPENSSL_ECDH_H */ +#endif /* HAVE_LIBCRYPTO */ + +int ssh_client_ecdh_init(ssh_session session); +int ssh_client_ecdh_reply(ssh_session session, ssh_buffer packet); + + +#endif /* ECDH_H_ */ diff --git a/include/libssh/libcrypto.h b/include/libssh/libcrypto.h index 12dd2229..f1986602 100644 --- a/include/libssh/libcrypto.h +++ b/include/libssh/libcrypto.h @@ -32,6 +32,7 @@ #include <openssl/md5.h> #include <openssl/hmac.h> typedef SHA_CTX* SHACTX; +typedef SHA256_CTX* SHA256CTX; typedef MD5_CTX* MD5CTX; typedef HMAC_CTX* HMACCTX; @@ -67,6 +68,10 @@ typedef BN_CTX* bignum_CTX; #define bignum_bn2bin(num,ptr) BN_bn2bin(num,ptr) #define bignum_cmp(num1,num2) BN_cmp(num1,num2) +SHA256CTX sha256_init(void); +void sha256_update(SHA256CTX c, const void *data, unsigned long len); +void sha256_final(unsigned char *md, SHA256CTX c); + struct crypto_struct *ssh_get_ciphertab(void); #endif /* HAVE_LIBCRYPTO */ diff --git a/include/libssh/libgcrypt.h b/include/libssh/libgcrypt.h index 0b1b16da..3afbcb49 100644 --- a/include/libssh/libgcrypt.h +++ b/include/libssh/libgcrypt.h @@ -30,8 +30,13 @@ typedef gcry_md_hd_t SHACTX; typedef gcry_md_hd_t MD5CTX; typedef gcry_md_hd_t HMACCTX; -#define SHA_DIGEST_LEN 20 +#define SHA_DIGEST_LENGTH 20 +#define SHA_DIGEST_LEN SHA_DIGEST_LENGTH #define MD5_DIGEST_LEN 16 +#define SHA256_DIGEST_LENGTH 32 +#define SHA384_DIGEST_LENGTH 48 +#define SHA512_DIGEST_LENGTH 64 + #define EVP_MAX_MD_SIZE 36 typedef gcry_mpi_t bignum; diff --git a/include/libssh/session.h b/include/libssh/session.h index a31e6bfe..2d4b05bc 100644 --- a/include/libssh/session.h +++ b/include/libssh/session.h @@ -103,7 +103,7 @@ struct ssh_session_struct { enum ssh_auth_service_state_e auth_service_state; enum ssh_auth_state_e auth_state; enum ssh_channel_request_state_e global_req_state; - ssh_string dh_server_signature; /* information used by dh_handshake. */ + KEX server_kex; KEX client_kex; ssh_buffer in_hashbuf; diff --git a/include/libssh/ssh2.h b/include/libssh/ssh2.h index bd89f4e1..f66dd2a9 100644 --- a/include/libssh/ssh2.h +++ b/include/libssh/ssh2.h @@ -13,6 +13,10 @@ #define SSH2_MSG_KEXDH_INIT 30 #define SSH2_MSG_KEXDH_REPLY 31 +#define SSH2_MSG_KEX_ECDH_INIT 30 +#define SSH2_MSG_KEX_ECDH_REPLY 31 +#define SSH2_MSG_ECMQV_INIT 30 +#define SSH2_MSG_ECMQV_REPLY 31 #define SSH2_MSG_KEX_DH_GEX_REQUEST_OLD 30 #define SSH2_MSG_KEX_DH_GEX_GROUP 31 diff --git a/include/libssh/wrapper.h b/include/libssh/wrapper.h index 8c034970..7fe6f8ad 100644 --- a/include/libssh/wrapper.h +++ b/include/libssh/wrapper.h @@ -25,7 +25,21 @@ #include "config.h" #include "libssh/libcrypto.h" #include "libssh/libgcrypt.h" +#include "libssh/crypto.h" +enum ssh_mac_e { + SSH_MAC_SHA1=1, + SSH_MAC_SHA256, + SSH_MAC_SHA384, + SSH_MAC_SHA512 +}; + +enum ssh_hmac_e { + SSH_HMAC_SHA1 = 1, + SSH_HMAC_MD5 +}; + +typedef struct ssh_mac_ctx_struct *ssh_mac_ctx; MD5CTX md5_init(void); void md5_update(MD5CTX c, const void *data, unsigned long len); void md5_final(unsigned char *md,MD5CTX c); @@ -33,9 +47,13 @@ SHACTX sha1_init(void); void sha1_update(SHACTX c, const void *data, unsigned long len); void sha1_final(unsigned char *md,SHACTX c); void sha1(unsigned char *digest,int len,unsigned char *hash); -#define HMAC_SHA1 1 -#define HMAC_MD5 2 -HMACCTX hmac_init(const void *key,int len,int type); +void sha256(unsigned char *digest, int len, unsigned char *hash); + +ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type); +void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len); +void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx); + +HMACCTX hmac_init(const void *key,int len, enum ssh_hmac_e type); void hmac_update(HMACCTX c, const void *data, unsigned long len); void hmac_final(HMACCTX ctx,unsigned char *hashmacbuf,unsigned int *len); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 64eebf0a..103b007a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -85,6 +85,7 @@ set(libssh_SRCS crc32.c crypt.c dh.c + ecdh.c error.c getpass.c gzip.c diff --git a/src/client.c b/src/client.c index 7b2e4494..426dd2b3 100644 --- a/src/client.c +++ b/src/client.c @@ -36,6 +36,7 @@ #include "libssh/socket.h" #include "libssh/session.h" #include "libssh/dh.h" +#include "libssh/ecdh.h" #include "libssh/threads.h" #include "libssh/misc.h" @@ -176,12 +177,8 @@ end: return err; } - - SSH_PACKET_CALLBACK(ssh_packet_dh_reply){ - ssh_string f = NULL; - ssh_string pubkey = NULL; - ssh_string signature = NULL; + int rc; (void)type; (void)user; ssh_log(session,SSH_LOG_PROTOCOL,"Received SSH_KEXDH_REPLY"); @@ -191,48 +188,23 @@ SSH_PACKET_CALLBACK(ssh_packet_dh_reply){ session->session_state,session->dh_handshake_state); goto error; } - - pubkey = buffer_get_ssh_string(packet); - if (pubkey == NULL){ - ssh_set_error(session,SSH_FATAL, "No public key in packet"); - goto error; - } - dh_import_pubkey(session, pubkey); - - f = buffer_get_ssh_string(packet); - if (f == NULL) { - ssh_set_error(session,SSH_FATAL, "No F number in packet"); - goto error; - } - if (dh_import_f(session, f) < 0) { - ssh_set_error(session, SSH_FATAL, "Cannot import f number"); - goto error; - } - ssh_string_burn(f); - ssh_string_free(f); - f=NULL; - signature = buffer_get_ssh_string(packet); - if (signature == NULL) { - ssh_set_error(session, SSH_FATAL, "No signature in packet"); - goto error; - } - session->dh_server_signature = signature; - signature=NULL; /* ownership changed */ - if (dh_build_k(session) < 0) { - ssh_set_error(session, SSH_FATAL, "Cannot build k number"); - goto error; + switch(session->next_crypto->kex_type){ + case SSH_KEX_DH_GROUP1_SHA1: + rc=ssh_client_dh_reply(session, packet); + break; +#ifdef HAVE_ECDH + case SSH_KEX_ECDH_SHA2_NISTP256: + rc = ssh_client_ecdh_reply(session, packet); + break; +#endif + default: + ssh_set_error(session,SSH_FATAL,"Wrong kex type in ssh_packet_dh_reply"); + goto error; } - - /* Send the MSG_NEWKEYS */ - if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { - goto error; + if(rc==SSH_OK) { + session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; + return SSH_PACKET_USED; } - - packet_send(session); - ssh_log(session, SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); - - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; - return SSH_PACKET_USED; error: session->session_state=SSH_SESSION_STATE_ERROR; return SSH_PACKET_USED; @@ -274,12 +246,12 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys){ } /* Verify the host's signature. FIXME do it sooner */ - signature = session->dh_server_signature; - session->dh_server_signature = NULL; + signature = session->next_crypto->dh_server_signature; + session->next_crypto->dh_server_signature = NULL; if (signature_verify(session, signature)) { goto error; } - + ssh_log(session,SSH_LOG_PROTOCOL,"Signature verified and valid"); /* forget it for now ... */ ssh_string_burn(signature); ssh_string_free(signature); @@ -325,7 +297,20 @@ static int dh_handshake(ssh_session session) { switch (session->dh_handshake_state) { case DH_STATE_INIT: - rc = ssh_client_dh_init(session); + switch(session->next_crypto->kex_type){ + case SSH_KEX_DH_GROUP1_SHA1: + rc = ssh_client_dh_init(session); + break; +#ifdef HAVE_ECDH + case SSH_KEX_ECDH_SHA2_NISTP256: + rc = ssh_client_ecdh_init(session); + break; +#endif + default: + rc=SSH_ERROR; + goto error; + } + if (rc == SSH_ERROR) { goto error; } diff --git a/src/crypt.c b/src/crypt.c index 363517e6..4c2e465c 100644 --- a/src/crypt.c +++ b/src/crypt.c @@ -136,7 +136,7 @@ unsigned char *packet_encrypt(ssh_session session, void *data, uint32_t len) { #endif if (session->version == 2) { - ctx = hmac_init(session->current_crypto->encryptMAC,20,HMAC_SHA1); + ctx = hmac_init(session->current_crypto->encryptMAC,20,SSH_HMAC_SHA1); if (ctx == NULL) { SAFE_FREE(out); return NULL; @@ -190,7 +190,7 @@ int packet_hmac_verify(ssh_session session, ssh_buffer buffer, unsigned int len; uint32_t seq; - ctx = hmac_init(session->current_crypto->decryptMAC, 20, HMAC_SHA1); + ctx = hmac_init(session->current_crypto->decryptMAC, 20, SSH_HMAC_SHA1); if (ctx == NULL) { return -1; } @@ -521,6 +521,56 @@ int ssh_client_dh_init(ssh_session session){ leave_function(); return SSH_ERROR; } + +int ssh_client_dh_reply(ssh_session session, ssh_buffer packet){ + ssh_string f = NULL; + ssh_string pubkey = NULL; + ssh_string signature = NULL; + int rc; + pubkey = buffer_get_ssh_string(packet); + if (pubkey == NULL){ + ssh_set_error(session,SSH_FATAL, "No public key in packet"); + goto error; + } + dh_import_pubkey(session, pubkey); + + f = buffer_get_ssh_string(packet); + if (f == NULL) { + ssh_set_error(session,SSH_FATAL, "No F number in packet"); + goto error; + } + if (dh_import_f(session, f) < 0) { + ssh_set_error(session, SSH_FATAL, "Cannot import f number"); + goto error; + } + ssh_string_burn(f); + ssh_string_free(f); + f=NULL; + signature = buffer_get_ssh_string(packet); + if (signature == NULL) { + ssh_set_error(session, SSH_FATAL, "No signature in packet"); + goto error; + } + session->next_crypto->dh_server_signature = signature; + signature=NULL; /* ownership changed */ + if (dh_build_k(session) < 0) { + ssh_set_error(session, SSH_FATAL, "Cannot build k number"); + goto error; + } + + /* Send the MSG_NEWKEYS */ + if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { + goto error; + } + + rc=packet_send(session); + ssh_log(session, SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + return rc; +error: + return SSH_ERROR; +} + + /* static void sha_add(ssh_string str,SHACTX ctx){ sha1_update(ctx,str,string_len(str)+4); @@ -531,7 +581,6 @@ static void sha_add(ssh_string str,SHACTX ctx){ */ int make_sessionid(ssh_session session) { - SHACTX ctx; ssh_string num = NULL; ssh_string str = NULL; ssh_buffer server_hash = NULL; @@ -542,11 +591,6 @@ int make_sessionid(ssh_session session) { enter_function(); - ctx = sha1_init(); - if (ctx == NULL) { - return rc; - } - buf = ssh_buffer_new(); if (buf == NULL) { return rc; @@ -614,29 +658,33 @@ int make_sessionid(ssh_session session) { if (buffer_add_data(buf, session->next_crypto->server_pubkey, len) < 0) { goto error; } + if(session->next_crypto->kex_type == SSH_KEX_DH_GROUP1_SHA1){ + num = make_bignum_string(session->next_crypto->e); + if (num == NULL) { + goto error; + } - num = make_bignum_string(session->next_crypto->e); - if (num == NULL) { - goto error; - } + len = ssh_string_len(num) + 4; + if (buffer_add_data(buf, num, len) < 0) { + goto error; + } - len = ssh_string_len(num) + 4; - if (buffer_add_data(buf, num, len) < 0) { - goto error; - } + ssh_string_free(num); + num = make_bignum_string(session->next_crypto->f); + if (num == NULL) { + goto error; + } - ssh_string_free(num); - num = make_bignum_string(session->next_crypto->f); - if (num == NULL) { - goto error; - } + len = ssh_string_len(num) + 4; + if (buffer_add_data(buf, num, len) < 0) { + goto error; + } - len = ssh_string_len(num) + 4; - if (buffer_add_data(buf, num, len) < 0) { - goto error; + ssh_string_free(num); + } else if (session->next_crypto->kex_type == SSH_KEX_ECDH_SHA2_NISTP256){ + buffer_add_ssh_string(buf,session->next_crypto->ecdh_client_pubkey); + buffer_add_ssh_string(buf,session->next_crypto->ecdh_server_pubkey); } - - ssh_string_free(num); num = make_bignum_string(session->next_crypto->k); if (num == NULL) { goto error; @@ -651,8 +699,31 @@ int make_sessionid(ssh_session session) { ssh_print_hexa("hash buffer", ssh_buffer_get_begin(buf), ssh_buffer_get_len(buf)); #endif - sha1_update(ctx, buffer_get_rest(buf), buffer_get_rest_len(buf)); - sha1_final(session->next_crypto->session_id, ctx); + switch(session->next_crypto->kex_type){ + case SSH_KEX_DH_GROUP1_SHA1: + session->next_crypto->digest_len = SHA_DIGEST_LENGTH; + session->next_crypto->mac_type = SSH_MAC_SHA1; + 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; + } + sha1(buffer_get_rest(buf), buffer_get_rest_len(buf), + session->next_crypto->session_id); + break; + case SSH_KEX_ECDH_SHA2_NISTP256: + session->next_crypto->digest_len = SHA256_DIGEST_LENGTH; + session->next_crypto->mac_type = SSH_MAC_SHA256; + 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; + } + sha256(buffer_get_rest(buf), buffer_get_rest_len(buf), + session->next_crypto->session_id); + break; + } + #ifdef DEBUG_CRYPTO printf("Session hash: "); @@ -723,126 +794,134 @@ int hashbufin_add_cookie(ssh_session session, unsigned char *cookie) { } static int generate_one_key(ssh_string k, - unsigned char session_id[SHA_DIGEST_LEN], - unsigned char output[SHA_DIGEST_LEN], - char letter) { - SHACTX ctx = NULL; + struct ssh_crypto_struct *crypto, unsigned char *output, char letter) { + ssh_mac_ctx ctx; + ctx=ssh_mac_ctx_init(crypto->mac_type); - ctx = sha1_init(); if (ctx == NULL) { return -1; } - sha1_update(ctx, k, ssh_string_len(k) + 4); - sha1_update(ctx, session_id, SHA_DIGEST_LEN); - sha1_update(ctx, &letter, 1); - sha1_update(ctx, session_id, SHA_DIGEST_LEN); - sha1_final(output, ctx); + ssh_mac_update(ctx, k, ssh_string_len(k) + 4); + ssh_mac_update(ctx, crypto->session_id, crypto->digest_len); + ssh_mac_update(ctx, &letter, 1); + ssh_mac_update(ctx, crypto->session_id, crypto->digest_len); + ssh_mac_final(output, ctx); return 0; } int generate_session_keys(ssh_session session) { ssh_string k_string = NULL; - SHACTX ctx = NULL; + ssh_mac_ctx ctx = NULL; + struct ssh_crypto_struct *crypto = session->next_crypto; int rc = -1; enter_function(); - k_string = make_bignum_string(session->next_crypto->k); + k_string = 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) { - if (generate_one_key(k_string, session->next_crypto->session_id, - session->next_crypto->encryptIV, 'A') < 0) { + if (generate_one_key(k_string, crypto, crypto->encryptIV, 'A') < 0) { goto error; } - if (generate_one_key(k_string, session->next_crypto->session_id, - session->next_crypto->decryptIV, 'B') < 0) { + if (generate_one_key(k_string, crypto, crypto->decryptIV, 'B') < 0) { goto error; } } else { - if (generate_one_key(k_string, session->next_crypto->session_id, - session->next_crypto->decryptIV, 'A') < 0) { + if (generate_one_key(k_string, crypto, crypto->decryptIV, 'A') < 0) { goto error; } - if (generate_one_key(k_string, session->next_crypto->session_id, - session->next_crypto->encryptIV, 'B') < 0) { + if (generate_one_key(k_string, crypto, crypto->encryptIV, 'B') < 0) { goto error; } } if (session->client) { - if (generate_one_key(k_string, session->next_crypto->session_id, - session->next_crypto->encryptkey, 'C') < 0) { + if (generate_one_key(k_string, crypto, crypto->encryptkey, 'C') < 0) { goto error; } - if (generate_one_key(k_string, session->next_crypto->session_id, - session->next_crypto->decryptkey, 'D') < 0) { + if (generate_one_key(k_string, crypto, crypto->decryptkey, 'D') < 0) { goto error; } } else { - if (generate_one_key(k_string, session->next_crypto->session_id, - session->next_crypto->decryptkey, 'C') < 0) { + if (generate_one_key(k_string, crypto, crypto->decryptkey, 'C') < 0) { goto error; } - if (generate_one_key(k_string, session->next_crypto->session_id, - session->next_crypto->encryptkey, 'D') < 0) { + if (generate_one_key(k_string, crypto, crypto->encryptkey, 'D') < 0) { goto error; } } - /* some ciphers need more than 20 bytes of input key */ - /* XXX verify it's ok for server implementation */ - if (session->next_crypto->out_cipher->keysize > SHA_DIGEST_LEN * 8) { - ctx = sha1_init(); + /* some ciphers need more than DIGEST_LEN bytes of input key */ + if (crypto->out_cipher->keysize > crypto->digest_len * 8) { + crypto->encryptkey = realloc(crypto->encryptkey, crypto->digest_len * 2); + if(crypto->encryptkey == NULL) + goto error; + ctx = ssh_mac_ctx_init(crypto->mac_type); if (ctx == NULL) { goto error; } - sha1_update(ctx, k_string, ssh_string_len(k_string) + 4); - sha1_update(ctx, session->next_crypto->session_id, SHA_DIGEST_LEN); - sha1_update(ctx, session->next_crypto->encryptkey, SHA_DIGEST_LEN); - sha1_final(session->next_crypto->encryptkey + SHA_DIGEST_LEN, ctx); + ssh_mac_update(ctx, k_string, ssh_string_len(k_string) + 4); + ssh_mac_update(ctx, crypto->session_id, + crypto->digest_len); + ssh_mac_update(ctx, crypto->encryptkey, crypto->digest_len); + ssh_mac_final(crypto->encryptkey + crypto->digest_len, ctx); } - if (session->next_crypto->in_cipher->keysize > SHA_DIGEST_LEN * 8) { - ctx = sha1_init(); - sha1_update(ctx, k_string, ssh_string_len(k_string) + 4); - sha1_update(ctx, session->next_crypto->session_id, SHA_DIGEST_LEN); - sha1_update(ctx, session->next_crypto->decryptkey, SHA_DIGEST_LEN); - sha1_final(session->next_crypto->decryptkey + SHA_DIGEST_LEN, ctx); + if (crypto->in_cipher->keysize > crypto->digest_len * 8) { + crypto->decryptkey = realloc(crypto->decryptkey, crypto->digest_len *2); + if(crypto->decryptkey == NULL) + goto error; + ctx = ssh_mac_ctx_init(crypto->mac_type); + ssh_mac_update(ctx, k_string, ssh_string_len(k_string) + 4); + ssh_mac_update(ctx, crypto->session_id, + crypto->digest_len); + ssh_mac_update(ctx, crypto->decryptkey, crypto->digest_len); + ssh_mac_final(crypto->decryptkey + crypto->digest_len, ctx); } if(session->client) { - if (generate_one_key(k_string, session->next_crypto->session_id, - session->next_crypto->encryptMAC, 'E') < 0) { + if (generate_one_key(k_string, crypto, crypto->encryptMAC, 'E') < 0) { goto error; } - if (generate_one_key(k_string, session->next_crypto->session_id, - session->next_crypto->decryptMAC, 'F') < 0) { + if (generate_one_key(k_string, crypto, crypto->decryptMAC, 'F') < 0) { goto error; } } else { - if (generate_one_key(k_string, session->next_crypto->session_id, - session->next_crypto->decryptMAC, 'E') < 0) { + if (generate_one_key(k_string, crypto, crypto->decryptMAC, 'E') < 0) { goto error; } - if (generate_one_key(k_string, session->next_crypto->session_id, - session->next_crypto->encryptMAC, 'F') < 0) { + if (generate_one_key(k_string, crypto, crypto->encryptMAC, 'F') < 0) { goto error; } } #ifdef DEBUG_CRYPTO - ssh_print_hexa("Encrypt IV", session->next_crypto->encryptIV, SHA_DIGEST_LEN); - ssh_print_hexa("Decrypt IV", session->next_crypto->decryptIV, SHA_DIGEST_LEN); - ssh_print_hexa("Encryption key", session->next_crypto->encryptkey, - session->next_crypto->out_cipher->keysize); - ssh_print_hexa("Decryption key", session->next_crypto->decryptkey, - session->next_crypto->in_cipher->keysize); - ssh_print_hexa("Encryption MAC", session->next_crypto->encryptMAC, SHA_DIGEST_LEN); - ssh_print_hexa("Decryption MAC", session->next_crypto->decryptMAC, 20); + ssh_print_hexa("Encrypt IV", crypto->encryptIV, SHA_DIGEST_LEN); + ssh_print_hexa("Decrypt IV", crypto->decryptIV, SHA_DIGEST_LEN); + ssh_print_hexa("Encryption key", crypto->encryptkey, + crypto->out_cipher->keysize); + ssh_print_hexa("Decryption key", crypto->decryptkey, + crypto->in_cipher->keysize); + ssh_print_hexa("Encryption MAC", crypto->encryptMAC, SHA_DIGEST_LEN); + ssh_print_hexa("Decryption MAC", crypto->decryptMAC, 20); #endif rc = 0; @@ -1090,7 +1169,7 @@ int signature_verify(ssh_session session, ssh_string signature) { "Going to verify a %s type signature", pubkey->type_c); err = sig_verify(session,pubkey,sign, - session->next_crypto->session_id,SHA_DIGEST_LEN); + session->next_crypto->session_id, session->next_crypto->digest_len); signature_free(sign); session->next_crypto->server_pubkey_type = pubkey->type_c; publickey_free(pubkey); diff --git a/src/ecdh.c b/src/ecdh.c new file mode 100644 index 00000000..20840c8d --- /dev/null +++ b/src/ecdh.c @@ -0,0 +1,167 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2011 by Aris Adamantiadis + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +#include "config.h" +#include "libssh/session.h" +#include "libssh/ecdh.h" +#include "libssh/buffer.h" +#include "libssh/ssh2.h" + +#ifdef HAVE_ECDH +#include <openssl/ecdh.h> + +#define NISTP256 NID_X9_62_prime256v1 +#define NISTP384 NID_secp384r1 +#define NISTP521 NID_secp521r1 + +/** @internal + * @brief Starts ecdh-sha2-nistp256 key exchange + */ +int ssh_client_ecdh_init(ssh_session session){ + ssh_string e = NULL; + EC_KEY *key=NULL; + const EC_GROUP *group; + const EC_POINT *pubkey; + ssh_string client_pubkey; + int len; + int rc; + bignum_CTX ctx=BN_CTX_new(); + enter_function(); + if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT) < 0) { + goto error; + } + key = EC_KEY_new_by_curve_name(NISTP256); + group = EC_KEY_get0_group(key); + EC_KEY_generate_key(key); + pubkey=EC_KEY_get0_public_key(key); + len = EC_POINT_point2oct(group,pubkey,POINT_CONVERSION_UNCOMPRESSED, + NULL,0,ctx); + client_pubkey=ssh_string_new(len); + + EC_POINT_point2oct(group,pubkey,POINT_CONVERSION_UNCOMPRESSED, + ssh_string_data(client_pubkey),len,ctx); + buffer_add_ssh_string(session->out_buffer,client_pubkey); + BN_CTX_free(ctx); + session->next_crypto->ecdh_privkey = key; + session->next_crypto->ecdh_client_pubkey = client_pubkey; + rc = packet_send(session); + return rc; +error: + if(e != NULL){ + ssh_string_burn(e); + ssh_string_free(e); + } + + leave_function(); + return SSH_ERROR; +} + +static void ecdh_import_pubkey(ssh_session session, ssh_string pubkey_string) { + session->next_crypto->server_pubkey = pubkey_string; +} + +static int ecdh_build_k(ssh_session session) { + const EC_GROUP *group = EC_KEY_get0_group(session->next_crypto->ecdh_privkey); + EC_POINT *pubkey=EC_POINT_new(group); + void *buffer; + int len = (EC_GROUP_get_degree(group) + 7) / 8; +#ifdef HAVE_LIBCRYPTO + bignum_CTX ctx = bignum_ctx_new(); + if (ctx == NULL) { + return -1; + } +#endif + + session->next_crypto->k = bignum_new(); + if (session->next_crypto->k == NULL) { +#ifdef HAVE_LIBCRYPTO + bignum_ctx_free(ctx); +#endif + return -1; + } + + EC_POINT_oct2point(group,pubkey,ssh_string_data(session->next_crypto->ecdh_server_pubkey), + ssh_string_len(session->next_crypto->ecdh_server_pubkey),ctx); + buffer = malloc(len); + ECDH_compute_key(buffer,len,pubkey,session->next_crypto->ecdh_privkey,NULL); + BN_bin2bn(buffer,len,session->next_crypto->k); + free(buffer); + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("Session server cookie", session->server_kex.cookie, 16); + ssh_print_hexa("Session client cookie", session->client_kex.cookie, 16); + ssh_print_bignum("Shared secret key", session->next_crypto->k); +#endif + +#ifdef HAVE_LIBCRYPTO + bignum_ctx_free(ctx); +#endif + + return 0; +} + +/** @internal + * @brief parses a SSH_MSG_KEX_ECDH_REPLY packet and sends back + * a SSH_MSG_NEWKEYS + */ +int ssh_client_ecdh_reply(ssh_session session, ssh_buffer packet){ + ssh_string q_s_string = NULL; + ssh_string pubkey = NULL; + ssh_string signature = NULL; + int rc; + pubkey = buffer_get_ssh_string(packet); + if (pubkey == NULL){ + ssh_set_error(session,SSH_FATAL, "No public key in packet"); + goto error; + } + ecdh_import_pubkey(session, pubkey); + + q_s_string = buffer_get_ssh_string(packet); + if (q_s_string == NULL) { + ssh_set_error(session,SSH_FATAL, "No Q_S ECC point in packet"); + goto error; + } + session->next_crypto->ecdh_server_pubkey = q_s_string; + signature = buffer_get_ssh_string(packet); + if (signature == NULL) { + ssh_set_error(session, SSH_FATAL, "No signature in packet"); + goto error; + } + session->next_crypto->dh_server_signature = signature; + signature=NULL; /* ownership changed */ + if (ecdh_build_k(session) < 0) { + ssh_set_error(session, SSH_FATAL, "Cannot build k number"); + goto error; + } + + /* Send the MSG_NEWKEYS */ + if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { + goto error; + } + + rc=packet_send(session); + ssh_log(session, SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + return rc; +error: + return SSH_ERROR; +} + +#endif /* HAVE_ECDH */ @@ -42,6 +42,7 @@ #include "libssh/dh.h" #include "libssh/kex.h" #include "libssh/string.h" +#include "libssh/ecdh.h" #ifdef HAVE_LIBGCRYPT #define BLOWFISH "blowfish-cbc," @@ -72,8 +73,14 @@ #define ZLIB "none" #endif +#ifdef HAVE_ECDH +#define KEY_EXCHANGE "ecdh-sha2-nistp256,diffie-hellman-group1-sha1" +#else +#define KEY_EXCHANGE "diffie-hellman-group1-sha1" +#endif + const char *default_methods[] = { - "diffie-hellman-group1-sha1", + KEY_EXCHANGE, "ssh-rsa,ssh-dss", AES BLOWFISH DES, AES BLOWFISH DES, @@ -87,7 +94,7 @@ const char *default_methods[] = { }; const char *supported_methods[] = { - "diffie-hellman-group1-sha1", + KEY_EXCHANGE, "ssh-rsa,ssh-dss", AES BLOWFISH DES, AES BLOWFISH DES, @@ -370,7 +377,7 @@ int set_kex(ssh_session session){ ssh_get_random(client->cookie,16,0); client->methods=malloc(10 * sizeof(char **)); if (client->methods == NULL) { - ssh_set_error(session, SSH_FATAL, "No space left"); + ssh_set_error_oom(session); leave_function(); return -1; } @@ -394,6 +401,11 @@ int set_kex(ssh_session session){ } } } + if(strcmp(client->methods[SSH_KEX], "diffie-hellman-group1-sha1") == 0){ + session->next_crypto->kex_type=SSH_KEX_DH_GROUP1_SHA1; + } else if(strcmp(client->methods[SSH_KEX], "ecdh-sha2-nistp256") == 0){ + session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP256; + } leave_function(); return 0; } diff --git a/src/known_hosts.c b/src/known_hosts.c index 60241b7a..7c935a83 100644 --- a/src/known_hosts.c +++ b/src/known_hosts.c @@ -346,7 +346,7 @@ static int match_hashed_host(ssh_session session, const char *host, return 0; } - mac = hmac_init(buffer_get_rest(salt), buffer_get_rest_len(salt), HMAC_SHA1); + mac = hmac_init(buffer_get_rest(salt), buffer_get_rest_len(salt), SSH_HMAC_SHA1); if (mac == NULL) { ssh_buffer_free(salt); ssh_buffer_free(hash); diff --git a/src/libcrypto.c b/src/libcrypto.c index f43a91eb..ede0331b 100644 --- a/src/libcrypto.c +++ b/src/libcrypto.c @@ -57,6 +57,14 @@ #include "libssh/crypto.h" +struct ssh_mac_ctx_struct { + enum ssh_mac_e mac_type; + union { + SHACTX sha1_ctx; + SHA256CTX sha256_ctx; + } ctx; +}; + static int alloc_key(struct crypto_struct *cipher) { cipher->key = malloc(cipher->keylen); if (cipher->key == NULL) { @@ -89,6 +97,29 @@ void sha1(unsigned char *digest, int len, unsigned char *hash) { SHA1(digest, len, hash); } +SHA256CTX sha256_init(void){ + SHA256CTX c = malloc(sizeof(*c)); + if (c == NULL) { + return NULL; + } + SHA256_Init(c); + + return c; +} + +void sha256_update(SHA256CTX c, const void *data, unsigned long len){ + SHA256_Update(c,data,len); +} + +void sha256_final(unsigned char *md, SHA256CTX c) { + SHA256_Final(md, c); + SAFE_FREE(c); +} + +void sha256(unsigned char *digest, int len, unsigned char *hash) { + SHA256(digest, len, hash); +} + MD5CTX md5_init(void) { MD5CTX c = malloc(sizeof(*c)); if (c == NULL) { @@ -109,7 +140,56 @@ void md5_final(unsigned char *md, MD5CTX c) { SAFE_FREE(c); } -HMACCTX hmac_init(const void *key, int len, int type) { +ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type){ + ssh_mac_ctx ctx=malloc(sizeof(struct ssh_mac_ctx_struct)); + ctx->mac_type=type; + switch(type){ + case SSH_MAC_SHA1: + ctx->ctx.sha1_ctx = sha1_init(); + return ctx; + case SSH_MAC_SHA256: + ctx->ctx.sha256_ctx = sha256_init(); + return ctx; + case SSH_MAC_SHA384: + case SSH_MAC_SHA512: + default: + SAFE_FREE(ctx); + return NULL; + } +} + +void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len) { + switch(ctx->mac_type){ + case SSH_MAC_SHA1: + sha1_update(ctx->ctx.sha1_ctx, data, len); + break; + case SSH_MAC_SHA256: + sha256_update(ctx->ctx.sha256_ctx, data, len); + break; + case SSH_MAC_SHA384: + case SSH_MAC_SHA512: + default: + break; + } +} + +void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx) { + switch(ctx->mac_type){ + case SSH_MAC_SHA1: + sha1_final(md,ctx->ctx.sha1_ctx); + break; + case SSH_MAC_SHA256: + sha256_final(md,ctx->ctx.sha256_ctx); + break; + case SSH_MAC_SHA384: + case SSH_MAC_SHA512: + default: + break; + } + SAFE_FREE(ctx); +} + +HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) { HMACCTX ctx = NULL; ctx = malloc(sizeof(*ctx)); @@ -122,10 +202,10 @@ HMACCTX hmac_init(const void *key, int len, int type) { #endif switch(type) { - case HMAC_SHA1: + case SSH_HMAC_SHA1: HMAC_Init(ctx, key, len, EVP_sha1()); break; - case HMAC_MD5: + case SSH_HMAC_MD5: HMAC_Init(ctx, key, len, EVP_md5()); break; default: diff --git a/src/libgcrypt.c b/src/libgcrypt.c index f8fe96f2..2e2695c8 100644 --- a/src/libgcrypt.c +++ b/src/libgcrypt.c @@ -31,6 +31,10 @@ #ifdef HAVE_LIBGCRYPT #include <gcrypt.h> +struct ssh_mac_ctx_struct { + enum ssh_mac_e mac_type; + gcry_md_hd_t ctx; +}; static int alloc_key(struct crypto_struct *cipher) { cipher->key = malloc(cipher->keylen); @@ -62,6 +66,10 @@ void sha1(unsigned char *digest, int len, unsigned char *hash) { gcry_md_hash_buffer(GCRY_MD_SHA1, hash, digest, len); } +void sha256(unsigned char *digest, int len, unsigned char *hash){ + gcry_md_hash_buffer(GCRY_MD_SHA256, hash, digest, len); +} + MD5CTX md5_init(void) { MD5CTX c = NULL; gcry_md_open(&c, GCRY_MD_MD5, 0); @@ -79,14 +87,63 @@ void md5_final(unsigned char *md, MD5CTX c) { gcry_md_close(c); } -HMACCTX hmac_init(const void *key, int len, int type) { +ssh_mac_ctx ssh_mac_ctx_init(enum ssh_mac_e type){ + ssh_mac_ctx ctx=malloc(sizeof(struct ssh_mac_ctx_struct)); + ctx->mac_type=type; + switch(type){ + case SSH_MAC_SHA1: + gcry_md_open(&ctx->ctx, GCRY_MD_SHA1, 0); + break; + case SSH_MAC_SHA256: + gcry_md_open(&ctx->ctx, GCRY_MD_SHA256, 0); + break; + case SSH_MAC_SHA384: + gcry_md_open(&ctx->ctx, GCRY_MD_SHA384, 0); + break; + case SSH_MAC_SHA512: + gcry_md_open(&ctx->ctx, GCRY_MD_SHA512, 0); + break; + default: + SAFE_FREE(ctx); + return NULL; + } + return ctx; +} + +void ssh_mac_update(ssh_mac_ctx ctx, const void *data, unsigned long len) { + gcry_md_write(ctx->ctx,data,len); +} + +void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx) { + size_t len; + switch(ctx->mac_type){ + case SSH_MAC_SHA1: + len=SHA_DIGEST_LEN; + break; + case SSH_MAC_SHA256: + len=SHA256_DIGEST_LENGTH; + break; + case SSH_MAC_SHA384: + len=SHA384_DIGEST_LENGTH; + break; + case SSH_MAC_SHA512: + len=SHA512_DIGEST_LENGTH; + break; + } + gcry_md_final(ctx->ctx); + memcpy(md, gcry_md_read(ctx->ctx, 0), len); + gcry_md_close(ctx->ctx); + SAFE_FREE(ctx); +} + +HMACCTX hmac_init(const void *key, int len, enum ssh_hmac_e type) { HMACCTX c = NULL; switch(type) { - case HMAC_SHA1: + case SSH_HMAC_SHA1: gcry_md_open(&c, GCRY_MD_SHA1, GCRY_MD_FLAG_HMAC); break; - case HMAC_MD5: + case SSH_HMAC_MD5: gcry_md_open(&c, GCRY_MD_MD5, GCRY_MD_FLAG_HMAC); break; default: |