diff options
author | Nicolas Viennot <nicolas@viennot.biz> | 2013-10-04 16:34:20 -0400 |
---|---|---|
committer | Nicolas Viennot <nicolas@viennot.biz> | 2013-10-04 16:34:36 -0400 |
commit | bb5634ce20ccf339d71ff6d5b7a4e8d668fd9c96 (patch) | |
tree | fc6a5e21be5744939aeeacff52324f078a29023e | |
parent | 8a87170c8ac10bfea9c2edfec6f0ece296edd7f3 (diff) |
Update libssh
-rw-r--r-- | libssh/CMakeLists.txt | 4 | ||||
-rw-r--r-- | libssh/DefineOptions.cmake | 1 | ||||
-rw-r--r-- | libssh/doc/curve25519-sha256@libssh.org.txt | 119 | ||||
-rw-r--r-- | libssh/include/libssh/callbacks.h | 27 | ||||
-rw-r--r-- | libssh/include/libssh/crypto.h | 10 | ||||
-rw-r--r-- | libssh/include/libssh/curve25519.h | 46 | ||||
-rw-r--r-- | libssh/include/libssh/session.h | 3 | ||||
-rw-r--r-- | libssh/src/CMakeLists.txt | 19 | ||||
-rw-r--r-- | libssh/src/channels.c | 36 | ||||
-rw-r--r-- | libssh/src/client.c | 13 | ||||
-rw-r--r-- | libssh/src/curve25519.c | 285 | ||||
-rw-r--r-- | libssh/src/dh.c | 13 | ||||
-rw-r--r-- | libssh/src/kex.c | 14 | ||||
-rw-r--r-- | libssh/src/messages.c | 11 | ||||
-rw-r--r-- | libssh/src/packet_cb.c | 6 | ||||
-rw-r--r-- | libssh/src/pki.c | 32 | ||||
-rw-r--r-- | libssh/src/server.c | 6 | ||||
-rw-r--r-- | libssh/src/session.c | 26 |
18 files changed, 623 insertions, 48 deletions
diff --git a/libssh/CMakeLists.txt b/libssh/CMakeLists.txt index e783c2b4..2c5c7232 100644 --- a/libssh/CMakeLists.txt +++ b/libssh/CMakeLists.txt @@ -109,7 +109,9 @@ install( # in tree build settings configure_file(libssh-build-tree-settings.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/libssh-build-tree-settings.cmake @ONLY) -add_subdirectory(examples) +if (WITH_EXAMPLES) + add_subdirectory(examples) +endif (WITH_EXAMPLES) if (WITH_TESTING) find_package(CMocka REQUIRED) diff --git a/libssh/DefineOptions.cmake b/libssh/DefineOptions.cmake index 6913f040..756b948a 100644 --- a/libssh/DefineOptions.cmake +++ b/libssh/DefineOptions.cmake @@ -12,6 +12,7 @@ option(WITH_INTERNAL_DOC "Compile doxygen internal documentation" OFF) option(WITH_TESTING "Build with unit tests" OFF) option(WITH_CLIENT_TESTING "Build with client tests; requires a running sshd" OFF) option(WITH_BENCHMARKS "Build benchmarks tools" OFF) +option(WITH_EXAMPLES "Build examples" ON) if (WITH_ZLIB) set(WITH_LIBZ ON) diff --git a/libssh/doc/curve25519-sha256@libssh.org.txt b/libssh/doc/curve25519-sha256@libssh.org.txt new file mode 100644 index 00000000..e68dc5e6 --- /dev/null +++ b/libssh/doc/curve25519-sha256@libssh.org.txt @@ -0,0 +1,119 @@ +curve25519-sha256@libssh.org.txt Aris Adamantiadis <aris@badcode.be> + 21/9/2013 + +1. Introduction + +This document describes the key exchange methode curve25519-sha256@libssh.org +for SSH version 2 protocol. It is provided as an alternative to the existing +key exchange mechanisms based on either Diffie-Hellman or Elliptic Curve Diffie- +Hellman [RFC5656]. +The reason is the following : During summer of 2013, revelations from ex- +consultant at NSA Edward Snowden gave proof that NSA willingly inserts backdoors +into softwares, hardware components and published standards. While it is still +believed that the mathematics behind ECC cryptography are still sound and solid, +some people (including Bruce Schneier [SCHNEIER]), showed their lack of confidence +in NIST-published curves such as nistp256, nistp384, nistp521, for which constant +parameters (including the generator point) are defined without explanation. It +is also believed that NSA had a word to say in their definition. These curves +are not the most secure or fastest possible for their key sizes [DJB], and +researchers think it is possible that NSA have ways of cracking NIST curves. +It is also interesting to note that SSH belongs to the list of protocols the NSA +claims to be able to eavesdrop. Having a secure replacement would make passive +attacks much harder if such a backdoor exists. + +However an alternative exists in the form of Curve25519. This algorithm has been +proposed in 2006 by DJB [Curve25519]. Its main stengths are its speed, its +constant-time run time (and resistance against side-channel attacks), and its +lack of nebulous hard-coded constants. + +The reference version being used in this document is the one described in +[Curve25519] as implemented in the library NaCl [NaCl]. +This document does not attempts to provide alternatives to the ecdsa-sha1-* +authentication keys. + +2. Key exchange + +The key exchange procedure is very similar to the one described chapter 4 of +[RFC5656]. Public ephemeral keys are transmitted over SSH encapsulated into +standard SSH strings. + +The following is an overview of the key exchange process: + +Client Server +------ ------ +Generate ephemeral key pair. +SSH_MSG_KEX_ECDH_INIT --------> + Verify that client public key + length is 32 bytes. + Generate ephemeral key pair. + Compute shared secret. + Generate and sign exchange hash. + <-------- SSH_MSG_KEX_ECDH_REPLY +Verify that server public key length is 32 bytes. +* Verify host keys belong to server. +Compute shared secret. +Generate exchange hash. +Verify server's signature. + +* Optional but strongly recommanded as this protects against MITM attacks. + +This is implemented using the same messages as described in RFC5656 chapter 4 + +3. Method Name + +The name of this key exchange method is "curve25519-sha256@libssh.org". + +4. Implementation considerations + +The whole method is based on the curve25519 scalar multiplication. In this +method, a private key is a scalar of 256 bits, and a public key is a point +of 256 bits. + +4.1. Private key generation + +A 32 bytes private key should be generated for each new connection, + using a secure PRNG. The following actions must be done on the private key: + mysecret[0] &= 248; + mysecret[31] &= 127; + mysecret[31] |= 64; +In order to keep the key valid. However, many cryptographic libraries will do +this automatically. +It should be noted that, in opposition to NIST curves, no special validation +should be done to ensure the result is a valid and secure private key. + +4.2 Public key generation + +The 32 bytes public key of either a client or a server must be generated using +the 32 bytes private key and a common generator base. This base is defined as 9 +followed by all zeroes: + const unsigned char basepoint[32] = {9}; + +The public key is calculated using the cryptographic scalar multiplication: + const unsigned char privkey[32]; + unsigned char pubkey[32]; + crypto_scalarmult (pubkey, privkey, basepoint); +However some cryptographic libraries may provide a combined function: + crypto_scalarmult_base (pubkey, privkey); + +It should be noted that, in opposition to NIST curves, no special validation +should be done to ensure the received public keys are valid curves point. The +Curve25519 algorithm ensure that every possible public key maps to a valid +ECC Point. + +4.3 Shared secret generation + +The shared secret, k, is defined in SSH specifications to be a big integer. +This number is calculated using the following procedure: + + X is the 32 bytes point obtained by the scalar multiplication of the other + side's public key and the local private key scalar. + + The whole 32 bytes of the number X are then converted into a big integer k. + This conversion follows the network byte order. This step differs from + RFC5656. + +[RFC5656] http://tools.ietf.org/html/rfc5656 +[SCHNEIER] https://www.schneier.com/blog/archives/2013/09/the_nsa_is_brea.html#c1675929 +[DJB] http://cr.yp.to/talks/2013.05.31/slides-dan+tanja-20130531-4x3.pdf +[Curve25519] "Curve25519: new Diffie-Hellman speed records." + http://cr.yp.to/ecdh/curve25519-20060209.pdf
\ No newline at end of file diff --git a/libssh/include/libssh/callbacks.h b/libssh/include/libssh/callbacks.h index 7525b73d..131e8229 100644 --- a/libssh/include/libssh/callbacks.h +++ b/libssh/include/libssh/callbacks.h @@ -788,12 +788,17 @@ struct ssh_threads_callbacks_struct { }; /** - * @brief sets the thread callbacks necessary if your program is using - * libssh in a multithreaded fashion. This function must be called first, - * outside of any threading context (in your main() for instance), before - * ssh_init(). - * @param cb pointer to a ssh_threads_callbacks_struct structure, which contains - * the different callbacks to be set. + * @brief Set the thread callbacks structure. + * + * This is necessary if your program is using libssh in a multithreaded fashion. + * This function must be called first, outside of any threading context (in your + * main() function for instance), before you call ssh_init(). + * + * @param[in] cb A pointer to a ssh_threads_callbacks_struct structure, which + * contains the different callbacks to be set. + * + * @returns Always returns SSH_OK. + * * @see ssh_threads_callbacks_struct * @see SSH_THREADS_PTHREAD */ @@ -809,9 +814,13 @@ LIBSSH_API int ssh_threads_set_callbacks(struct ssh_threads_callbacks_struct LIBSSH_API struct ssh_threads_callbacks_struct *ssh_threads_get_pthread(void); /** - * @brief returns a pointer on the noop threads callbacks, to be used with - * ssh_threads_set_callbacks. These callbacks do nothing and are being used by - * default. + * @brief Get the noop threads callbacks structure + * + * This can be used with ssh_threads_set_callbacks. These callbacks do nothing + * and are being used by default. + * + * @return Always returns a valid pointer to the noop callbacks structure. + * * @see ssh_threads_set_callbacks */ LIBSSH_API struct ssh_threads_callbacks_struct *ssh_threads_get_noop(void); diff --git a/libssh/include/libssh/crypto.h b/libssh/include/libssh/crypto.h index 5376ca61..eaff2ffd 100644 --- a/libssh/include/libssh/crypto.h +++ b/libssh/include/libssh/crypto.h @@ -44,6 +44,7 @@ #endif #include "libssh/ecdh.h" #include "libssh/kex.h" +#include "libssh/curve25519.h" enum ssh_key_exchange_e { /* diffie-hellman-group1-sha1 */ @@ -51,7 +52,9 @@ enum ssh_key_exchange_e { /* diffie-hellman-group14-sha1 */ SSH_KEX_DH_GROUP14_SHA1, /* ecdh-sha2-nistp256 */ - SSH_KEX_ECDH_SHA2_NISTP256 + SSH_KEX_ECDH_SHA2_NISTP256, + /* curve25519-sha256@libssh.org */ + SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG }; struct ssh_crypto_struct { @@ -61,6 +64,11 @@ struct ssh_crypto_struct { ssh_string ecdh_client_pubkey; ssh_string ecdh_server_pubkey; #endif +#ifdef HAVE_CURVE25519 + ssh_curve25519_privkey curve25519_privkey; + ssh_curve25519_pubkey curve25519_client_pubkey; + ssh_curve25519_pubkey curve25519_server_pubkey; +#endif ssh_string dh_server_signature; /* information used by dh_handshake. */ size_t digest_len; /* len of all the fields below */ unsigned char *session_id; diff --git a/libssh/include/libssh/curve25519.h b/libssh/include/libssh/curve25519.h new file mode 100644 index 00000000..004210cb --- /dev/null +++ b/libssh/include/libssh/curve25519.h @@ -0,0 +1,46 @@ +/* + * This file is part of the SSH Library + * + * Copyright (c) 2013 by Aris Adamantiadis <aris@badcode.be> + * + * This 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, + * version 2.1 of the License. + * + * This 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 this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CURVE25519_H_ +#define CURVE25519_H_ + +#include "config.h" +#include "libssh.h" + +#ifdef WITH_NACL + +#define HAVE_CURVE25519 +#include <nacl/crypto_scalarmult_curve25519.h> +#define CURVE25519_PUBKEY_SIZE crypto_scalarmult_curve25519_BYTES +#define CURVE25519_PRIVKEY_SIZE crypto_scalarmult_curve25519_SCALARBYTES + +typedef unsigned char ssh_curve25519_pubkey[CURVE25519_PUBKEY_SIZE]; +typedef unsigned char ssh_curve25519_privkey[CURVE25519_PRIVKEY_SIZE]; + +#endif /* WITH_NACL */ + +int ssh_client_curve25519_init(ssh_session session); +int ssh_client_curve25519_reply(ssh_session session, ssh_buffer packet); + +#ifdef WITH_SERVER +int ssh_server_curve25519_init(ssh_session session, ssh_buffer packet); +#endif /* WITH_SERVER */ + +#endif /* CURVE25519_H_ */ diff --git a/libssh/include/libssh/session.h b/libssh/include/libssh/session.h index f0d580dc..281c7c66 100644 --- a/libssh/include/libssh/session.h +++ b/libssh/include/libssh/session.h @@ -97,9 +97,6 @@ struct ssh_session_struct { int openssh; uint32_t send_seq; uint32_t recv_seq; -/* status flags */ - int closed; - int closed_by_except; int connected; /* !=0 when the user got a session handle */ diff --git a/libssh/src/CMakeLists.txt b/libssh/src/CMakeLists.txt index 06b239fa..b4046805 100644 --- a/libssh/src/CMakeLists.txt +++ b/libssh/src/CMakeLists.txt @@ -76,6 +76,18 @@ if (WITH_GSSAPI AND GSSAPI_FOUND) ) endif (WITH_GSSAPI AND GSSAPI_FOUND) +if (WITH_NACL AND NACL_FOUND) + set(LIBSSH_PRIVATE_INCLUDE_DIRS + ${LIBSSH_PRIVATE_INCLUDE_DIRS} + ${NACL_INCLUDE_DIR} + ) + + set(LIBSSH_LINK_LIBRARIES + ${LIBSSH_LINK_LIBRARIES} + ${NACL_LIBRARY} + ) +endif (WITH_NACL AND NACL_FOUND) + set(LIBSSH_LINK_LIBRARIES ${LIBSSH_LINK_LIBRARIES} CACHE INTERNAL "libssh link libraries" @@ -192,6 +204,13 @@ if (WITH_GSSAPI AND GSSAPI_FOUND) ) endif (WITH_GSSAPI AND GSSAPI_FOUND) +if (WITH_NACL) + set(libssh_SRCS + ${libssh_SRCS} + curve25519.c + ) +endif (WITH_NACL) + include_directories( ${LIBSSH_PUBLIC_INCLUDE_DIRS} ${LIBSSH_PRIVATE_INCLUDE_DIRS} diff --git a/libssh/src/channels.c b/libssh/src/channels.c index f4376e83..81b2bdfb 100644 --- a/libssh/src/channels.c +++ b/libssh/src/channels.c @@ -312,7 +312,11 @@ static int channel_open(ssh_channel channel, const char *type_c, int window, type_c, channel->local_channel); pending: /* wait until channel is opened by server */ - err = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, ssh_channel_open_termination, channel); + err = ssh_handle_packets_termination(session, + SSH_TIMEOUT_DEFAULT, + ssh_channel_open_termination, + channel); + if (session->session_state == SSH_SESSION_STATE_ERROR) err = SSH_ERROR; end: @@ -1607,7 +1611,11 @@ static int channel_request(ssh_channel channel, const char *request, return SSH_OK; } pending: - rc = ssh_handle_packets_termination(session,SSH_TIMEOUT_USER, ssh_channel_request_termination, channel); + rc = ssh_handle_packets_termination(session, + SSH_TIMEOUT_DEFAULT, + ssh_channel_request_termination, + channel); + if(session->session_state == SSH_SESSION_STATE_ERROR || rc == SSH_ERROR) { channel->request_state = SSH_CHANNEL_REQ_STATE_ERROR; } @@ -1983,8 +1991,11 @@ static ssh_channel ssh_channel_accept(ssh_session session, int channeltype, struct ssh_iterator *iterator; int t; - for (t = timeout_ms; t >= 0; t -= 50) - { + /* + * We sleep for 50 ms in ssh_handle_packets() and later sleep for + * 50 ms. So we need to decrement by 100 ms. + */ + for (t = timeout_ms; t >= 0; t -= 100) { ssh_handle_packets(session, 50); if (session->ssh_message_list) { @@ -2145,8 +2156,11 @@ static int global_request(ssh_session session, const char *request, return SSH_OK; } pending: - rc = ssh_handle_packets_termination(session, SSH_TIMEOUT_USER, - ssh_global_request_termination, session); + rc = ssh_handle_packets_termination(session, + SSH_TIMEOUT_DEFAULT, + ssh_global_request_termination, + session); + if(rc==SSH_ERROR || session->session_state == SSH_SESSION_STATE_ERROR){ session->global_req_state = SSH_CHANNEL_REQ_STATE_ERROR; } @@ -2640,8 +2654,8 @@ static int ssh_channel_read_termination(void *s){ * @param[in] is_stderr A boolean value to mark reading from the stderr flow. * * @return The number of bytes read, 0 on end of file or SSH_ERROR - * on error. Can return 0 if nothing is available in nonblocking - * mode. + * on error. In nonblocking mode it Can return 0 if no data + * is available or SSH_AGAIN. * * @warning This function may return less than count bytes of data, and won't * block until count bytes have been read. @@ -2924,8 +2938,10 @@ int ssh_channel_get_exit_status(ssh_channel channel) { if(channel == NULL) { return SSH_ERROR; } - rc = ssh_handle_packets_termination(channel->session, SSH_TIMEOUT_USER, - ssh_channel_exit_status_termination, channel); + rc = ssh_handle_packets_termination(channel->session, + SSH_TIMEOUT_DEFAULT, + ssh_channel_exit_status_termination, + channel); if (rc == SSH_ERROR || channel->session->session_state == SSH_SESSION_STATE_ERROR) return SSH_ERROR; diff --git a/libssh/src/client.c b/libssh/src/client.c index 99a300b1..dad988ed 100644 --- a/libssh/src/client.c +++ b/libssh/src/client.c @@ -197,6 +197,11 @@ static int dh_handshake(ssh_session session) { rc = ssh_client_ecdh_init(session); break; #endif +#ifdef HAVE_CURVE25519 + case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG: + rc = ssh_client_curve25519_init(session); + break; +#endif default: rc = SSH_ERROR; } @@ -579,6 +584,14 @@ char *ssh_get_issue_banner(ssh_session session) { * @param[in] session The SSH session to use. * * @return The version number if available, 0 otherwise. + * + * @code + * int openssh = ssh_get_openssh_version(); + * + * if (openssh == SSH_INT_VERSION(6, 1, 0)) { + * printf("Version match!\m"); + * } + * @endcode */ int ssh_get_openssh_version(ssh_session session) { if (session == NULL) { diff --git a/libssh/src/curve25519.c b/libssh/src/curve25519.c new file mode 100644 index 00000000..653beee0 --- /dev/null +++ b/libssh/src/curve25519.c @@ -0,0 +1,285 @@ +/* + * curve25519.c - Curve25519 ECDH functions for key exchange + * curve25519-sha256@libssh.org + * + * This file is part of the SSH Library + * + * Copyright (c) 2013 by Aris Adamantiadis <aris@badcode.be> + * + * 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, version 2.1 of the License. + * + * 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/curve25519.h" +#ifdef HAVE_CURVE25519 + +#include "nacl/crypto_scalarmult_curve25519.h" +#include "libssh/ssh2.h" +#include "libssh/buffer.h" +#include "libssh/priv.h" +#include "libssh/session.h" +#include "libssh/crypto.h" +#include "libssh/dh.h" +#include "libssh/pki.h" + +/** @internal + * @brief Starts curve25519-sha256@libssh.org key exchange + */ +int ssh_client_curve25519_init(ssh_session session){ + ssh_string client_pubkey; + int rc; + + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_INIT); + if (rc < 0) { + return SSH_ERROR; + } + + rc = ssh_get_random(session->next_crypto->curve25519_privkey, CURVE25519_PRIVKEY_SIZE, 1); + if (rc == 0){ + ssh_set_error(session, SSH_FATAL, "PRNG error"); + return SSH_ERROR; + } + + crypto_scalarmult_curve25519_base(session->next_crypto->curve25519_client_pubkey, + session->next_crypto->curve25519_privkey); + client_pubkey = ssh_string_new(CURVE25519_PUBKEY_SIZE); + if (client_pubkey == NULL) { + return SSH_ERROR; + } + ssh_string_fill(client_pubkey, session->next_crypto->curve25519_client_pubkey, + CURVE25519_PUBKEY_SIZE); + rc = buffer_add_ssh_string(session->out_buffer,client_pubkey); + if (rc < 0) { + ssh_string_free(client_pubkey); + return SSH_ERROR; + } + + rc = packet_send(session); + + return rc; +} + +static int ssh_curve25519_build_k(ssh_session session) { + ssh_curve25519_pubkey k; + session->next_crypto->k = bignum_new(); + + if (session->next_crypto->k == NULL) { + return SSH_ERROR; + } + + if (session->server) + crypto_scalarmult_curve25519(k, session->next_crypto->curve25519_privkey, + session->next_crypto->curve25519_client_pubkey); + else + crypto_scalarmult_curve25519(k, session->next_crypto->curve25519_privkey, + session->next_crypto->curve25519_server_pubkey); + + BN_bin2bn(k, CURVE25519_PUBKEY_SIZE, session->next_crypto->k); + +#ifdef DEBUG_CRYPTO + ssh_print_hexa("Session server cookie", + session->next_crypto->server_kex.cookie, 16); + ssh_print_hexa("Session client cookie", + session->next_crypto->client_kex.cookie, 16); + ssh_print_bignum("Shared secret key", session->next_crypto->k); +#endif + + return 0; +} + +/** @internal + * @brief parses a SSH_MSG_KEX_ECDH_REPLY packet and sends back + * a SSH_MSG_NEWKEYS + */ +int ssh_client_curve25519_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; + } + /* this is the server host key */ + session->next_crypto->server_pubkey = pubkey; + pubkey = NULL; + + 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; + } + if (ssh_string_len(q_s_string) != CURVE25519_PUBKEY_SIZE){ + ssh_set_error(session, SSH_FATAL, "Incorrect size for server Curve25519 public key: %d", + ssh_string_len(q_s_string)); + ssh_string_free(q_s_string); + goto error; + } + memcpy(session->next_crypto->curve25519_server_pubkey, ssh_string_data(q_s_string), CURVE25519_PUBKEY_SIZE); + + 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 */ + /* TODO: verify signature now instead of waiting for NEWKEYS */ + if (ssh_curve25519_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(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + return rc; +error: + return SSH_ERROR; +} + +#ifdef WITH_SERVER + +/** @brief Parse a SSH_MSG_KEXDH_INIT packet (server) and send a + * SSH_MSG_KEXDH_REPLY + */ +int ssh_server_curve25519_init(ssh_session session, ssh_buffer packet){ + /* ECDH keys */ + ssh_string q_c_string; + ssh_string q_s_string; + + /* SSH host keys (rsa,dsa,ecdsa) */ + ssh_key privkey; + ssh_string sig_blob = NULL; + int rc; + + /* Extract the client pubkey from the init packet */ + q_c_string = buffer_get_ssh_string(packet); + if (q_c_string == NULL) { + ssh_set_error(session,SSH_FATAL, "No Q_C ECC point in packet"); + return SSH_ERROR; + } + if (ssh_string_len(q_c_string) != CURVE25519_PUBKEY_SIZE){ + ssh_set_error(session, SSH_FATAL, "Incorrect size for server Curve25519 public key: %d", + ssh_string_len(q_c_string)); + ssh_string_free(q_c_string); + return SSH_ERROR; + } + + memcpy(session->next_crypto->curve25519_client_pubkey, + ssh_string_data(q_c_string), CURVE25519_PUBKEY_SIZE); + ssh_string_free(q_c_string); + /* Build server's keypair */ + + rc = ssh_get_random(session->next_crypto->curve25519_privkey, CURVE25519_PRIVKEY_SIZE, 1); + if (rc == 0){ + ssh_set_error(session, SSH_FATAL, "PRNG error"); + return SSH_ERROR; + } + + crypto_scalarmult_curve25519_base(session->next_crypto->curve25519_server_pubkey, + session->next_crypto->curve25519_privkey); + + q_s_string = ssh_string_new(CURVE25519_PUBKEY_SIZE); + if (q_s_string == NULL) { + return SSH_ERROR; + } + + ssh_string_fill(q_s_string, session->next_crypto->curve25519_server_pubkey, + CURVE25519_PUBKEY_SIZE); + + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_KEX_ECDH_REPLY); + if (rc < 0) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + + /* build k and session_id */ + rc = ssh_curve25519_build_k(session); + if (rc < 0) { + ssh_set_error(session, SSH_FATAL, "Cannot build k number"); + return SSH_ERROR; + } + + /* privkey is not allocated */ + rc = ssh_get_key_params(session, &privkey); + if (rc == SSH_ERROR) { + return SSH_ERROR; + } + + rc = make_sessionid(session); + if (rc != SSH_OK) { + ssh_set_error(session, SSH_FATAL, "Could not create a session id"); + return SSH_ERROR; + } + + /* add host's public key */ + rc = buffer_add_ssh_string(session->out_buffer, + session->next_crypto->server_pubkey); + if (rc < 0) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + + /* add ecdh public key */ + rc = buffer_add_ssh_string(session->out_buffer, q_s_string); + ssh_string_free(q_s_string); + + if (rc < 0) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + /* add signature blob */ + sig_blob = ssh_srv_pki_do_sign_sessionid(session, privkey); + if (sig_blob == NULL) { + ssh_set_error(session, SSH_FATAL, "Could not sign the session id"); + return SSH_ERROR; + } + + rc = buffer_add_ssh_string(session->out_buffer, sig_blob); + ssh_string_free(sig_blob); + if (rc < 0) { + ssh_set_error_oom(session); + return SSH_ERROR; + } + + SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEX_ECDH_REPLY sent"); + rc = packet_send(session); + if (rc == SSH_ERROR) { + return SSH_ERROR; + } + + /* Send the MSG_NEWKEYS */ + rc = buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS); + if (rc < 0) { + return SSH_ERROR;; + } + + session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; + rc = packet_send(session); + SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); + + return rc; +} + +#endif /* WITH_SERVER */ + +#endif /* HAVE_CURVE25519 */ diff --git a/libssh/src/dh.c b/libssh/src/dh.c index aa01c198..f96a94a3 100644 --- a/libssh/src/dh.c +++ b/libssh/src/dh.c @@ -771,6 +771,18 @@ int make_sessionid(ssh_session session) { goto error; } #endif +#ifdef HAVE_CURVE25519 + } else if(session->next_crypto->kex_type == SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG){ + rc = buffer_add_u32(buf, htonl(CURVE25519_PUBKEY_SIZE)); + rc += buffer_add_data(buf, session->next_crypto->curve25519_client_pubkey, + CURVE25519_PUBKEY_SIZE); + rc += buffer_add_u32(buf, htonl(CURVE25519_PUBKEY_SIZE)); + rc += buffer_add_data(buf, session->next_crypto->curve25519_server_pubkey, + CURVE25519_PUBKEY_SIZE); + if (rc != SSH_OK) { + goto error; + } +#endif } num = make_bignum_string(session->next_crypto->k); if (num == NULL) { @@ -800,6 +812,7 @@ int make_sessionid(ssh_session session) { session->next_crypto->secret_hash); break; case SSH_KEX_ECDH_SHA2_NISTP256: + 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); diff --git a/libssh/src/kex.c b/libssh/src/kex.c index 4de5a658..7cd404f7 100644 --- a/libssh/src/kex.c +++ b/libssh/src/kex.c @@ -34,6 +34,7 @@ #include "libssh/session.h" #include "libssh/ssh2.h" #include "libssh/string.h" +#include "libssh/curve25519.h" #ifdef HAVE_LIBGCRYPT # define BLOWFISH "blowfish-cbc," @@ -63,14 +64,21 @@ #define ZLIB "none" #endif +#ifdef HAVE_CURVE25519 +#define CURVE25519 "curve25519-sha256@libssh.org," +#else +#define CURVE25519 "" +#endif + #ifdef HAVE_ECDH -#define KEY_EXCHANGE "ecdh-sha2-nistp256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1" +#define ECDH "ecdh-sha2-nistp256," #define HOSTKEYS "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss" #else -#define KEY_EXCHANGE "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1" #define HOSTKEYS "ssh-rsa,ssh-dss" +#define ECDH "" #endif +#define KEY_EXCHANGE CURVE25519 ECDH "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1" #define KEX_METHODS_SIZE 10 /* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */ @@ -412,6 +420,8 @@ int ssh_kex_select_methods (ssh_session session){ session->next_crypto->kex_type=SSH_KEX_DH_GROUP14_SHA1; } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "ecdh-sha2-nistp256") == 0){ session->next_crypto->kex_type=SSH_KEX_ECDH_SHA2_NISTP256; + } else if(strcmp(session->next_crypto->kex_methods[SSH_KEX], "curve25519-sha256@libssh.org") == 0){ + session->next_crypto->kex_type=SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG; } return SSH_OK; diff --git a/libssh/src/messages.c b/libssh/src/messages.c index 003ecf8e..73f39974 100644 --- a/libssh/src/messages.c +++ b/libssh/src/messages.c @@ -127,6 +127,17 @@ static int ssh_execute_server_request(ssh_session session, ssh_message msg) } return SSH_OK; + } else if (msg->auth_request.method == SSH_AUTH_METHOD_NONE && + ssh_callbacks_exists(session->server_callbacks, auth_none_function)) { + rc = session->server_callbacks->auth_none_function(session, + msg->auth_request.username, session->server_callbacks->userdata); + if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_PARTIAL){ + ssh_message_auth_reply_success(msg, rc == SSH_AUTH_PARTIAL); + } else { + ssh_message_reply_default(msg); + } + + return SSH_OK; } break; case SSH_REQUEST_CHANNEL_OPEN: diff --git a/libssh/src/packet_cb.c b/libssh/src/packet_cb.c index 4a8beb54..f5d4f055 100644 --- a/libssh/src/packet_cb.c +++ b/libssh/src/packet_cb.c @@ -35,6 +35,7 @@ #include "libssh/session.h" #include "libssh/socket.h" #include "libssh/ssh2.h" +#include "libssh/curve25519.h" /** * @internal @@ -103,6 +104,11 @@ SSH_PACKET_CALLBACK(ssh_packet_dh_rep |