mirror of
https://github.com/tmate-io/tmate.git
synced 2024-11-27 02:23:15 +01:00
Update libssh
This commit is contained in:
parent
8a87170c8a
commit
bb5634ce20
@ -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)
|
||||
|
||||
if (WITH_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif (WITH_EXAMPLES)
|
||||
|
||||
if (WITH_TESTING)
|
||||
find_package(CMocka REQUIRED)
|
||||
|
@ -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)
|
||||
|
119
libssh/doc/curve25519-sha256@libssh.org.txt
Normal file
119
libssh/doc/curve25519-sha256@libssh.org.txt
Normal file
@ -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
|
@ -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);
|
||||
|
@ -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 {
|
||||
@ -60,6 +63,11 @@ struct ssh_crypto_struct {
|
||||
EC_KEY *ecdh_privkey;
|
||||
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 */
|
||||
|
46
libssh/include/libssh/curve25519.h
Normal file
46
libssh/include/libssh/curve25519.h
Normal file
@ -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_ */
|
@ -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 */
|
||||
|
@ -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}
|
||||
|
@ -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;
|
||||
|
@ -196,6 +196,11 @@ static int dh_handshake(ssh_session session) {
|
||||
case SSH_KEX_ECDH_SHA2_NISTP256:
|
||||
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) {
|
||||
|
285
libssh/src/curve25519.c
Normal file
285
libssh/src/curve25519.c
Normal file
@ -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 */
|
@ -770,6 +770,18 @@ int make_sessionid(ssh_session session) {
|
||||
if (rc < 0) {
|
||||
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);
|
||||
@ -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);
|
||||
|
@ -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_ECDH
|
||||
#define KEY_EXCHANGE "ecdh-sha2-nistp256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
|
||||
#define HOSTKEYS "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss"
|
||||
#ifdef HAVE_CURVE25519
|
||||
#define CURVE25519 "curve25519-sha256@libssh.org,"
|
||||
#else
|
||||
#define KEY_EXCHANGE "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
|
||||
#define HOSTKEYS "ssh-rsa,ssh-dss"
|
||||
#define CURVE25519 ""
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ECDH
|
||||
#define ECDH "ecdh-sha2-nistp256,"
|
||||
#define HOSTKEYS "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss"
|
||||
#else
|
||||
#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;
|
||||
|
@ -126,6 +126,17 @@ static int ssh_execute_server_request(ssh_session session, ssh_message msg)
|
||||
ssh_message_reply_default(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;
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include "libssh/session.h"
|
||||
#include "libssh/socket.h"
|
||||
#include "libssh/ssh2.h"
|
||||
#include "libssh/curve25519.h"
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -102,6 +103,11 @@ SSH_PACKET_CALLBACK(ssh_packet_dh_reply){
|
||||
case SSH_KEX_ECDH_SHA2_NISTP256:
|
||||
rc = ssh_client_ecdh_reply(session, packet);
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_CURVE25519
|
||||
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
|
||||
rc = ssh_client_curve25519_reply(session, packet);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ssh_set_error(session,SSH_FATAL,"Wrong kex type in ssh_packet_dh_reply");
|
||||
|
@ -349,8 +349,8 @@ void ssh_signature_free(ssh_signature sig)
|
||||
*
|
||||
* @param[in] auth_data Private data passed to the auth function.
|
||||
*
|
||||
* @param[out] pkey A pointer where the key can be stored. You need
|
||||
* to free the memory.
|
||||
* @param[out] pkey A pointer where the allocated key can be stored. You
|
||||
* need to free the memory.
|
||||
*
|
||||
* @return SSH_ERROR in case of error, SSH_OK otherwise.
|
||||
*
|
||||
@ -397,8 +397,8 @@ int ssh_pki_import_privkey_base64(const char *b64_key,
|
||||
*
|
||||
* @param[in] auth_data Private data passed to the auth function.
|
||||
*
|
||||
* @param[out] pkey A pointer to store the ssh_key. You need to free the
|
||||
* key.
|
||||
* @param[out] pkey A pointer to store the allocated ssh_key. You need to
|
||||
* free the key.
|
||||
*
|
||||
* @returns SSH_OK on success, SSH_EOF if the file doesn't exist or permission
|
||||
* denied, SSH_ERROR otherwise.
|
||||
@ -684,8 +684,8 @@ fail:
|
||||
*
|
||||
* @param[in] type The type of the key to format.
|
||||
*
|
||||
* @param[out] pkey A pointer where the key can be stored. You need
|
||||
* to free the memory.
|
||||
* @param[out] pkey A pointer where the allocated key can be stored. You
|
||||
* need to free the memory.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR on error.
|
||||
*
|
||||
@ -728,8 +728,8 @@ int ssh_pki_import_pubkey_base64(const char *b64_key,
|
||||
* @param[in] key_blob The key blob to import as specified in RFC 4253 section
|
||||
* 6.6 "Public Key Algorithms".
|
||||
*
|
||||
* @param[out] pkey A pointer where the key can be stored. You need
|
||||
* to free the memory.
|
||||
* @param[out] pkey A pointer where the allocated key can be stored. You
|
||||
* need to free the memory.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR on error.
|
||||
*
|
||||
@ -789,8 +789,8 @@ fail:
|
||||
*
|
||||
* @param[in] filename The path to the public key.
|
||||
*
|
||||
* @param[out] pkey A pointer to store the public key. You need to free the
|
||||
* memory.
|
||||
* @param[out] pkey A pointer to store the allocated public key. You need to
|
||||
* free the memory.
|
||||
*
|
||||
* @returns SSH_OK on success, SSH_EOF if the file doesn't exist or permission
|
||||
* denied, SSH_ERROR otherwise.
|
||||
@ -875,17 +875,20 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
|
||||
|
||||
/**
|
||||
* @brief Generates a keypair.
|
||||
*
|
||||
* @param[in] type Type of key to create
|
||||
*
|
||||
* @param[in] parameter Parameter to the creation of key:
|
||||
* rsa : length of the key in bits (e.g. 1024, 2048, 4096)
|
||||
* dsa : length of the key in bits (e.g. 1024, 2048, 3072)
|
||||
* ecdsa : bits of the key (e.g. 256, 384, 512)
|
||||
* @param[out] pkey A pointer to store the private key. You need to free the
|
||||
* memory.
|
||||
* @param[out] pkey A pointer to store the allocated private key. You need
|
||||
* to free the memory.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR on error.
|
||||
*
|
||||
* @warning Generating a key pair may take some time.
|
||||
*/
|
||||
|
||||
int ssh_pki_generate(enum ssh_keytypes_e type, int parameter,
|
||||
ssh_key *pkey){
|
||||
int rc;
|
||||
@ -1000,7 +1003,8 @@ int ssh_pki_export_pubkey_blob(const ssh_key key,
|
||||
*
|
||||
* @param[in] key The key to hash
|
||||
*
|
||||
* @param[out] b64_key A pointer to store the base64 hased key.
|
||||
* @param[out] b64_key A pointer to store the allocated base64 hashed key. You
|
||||
* need to free the buffer.
|
||||
*
|
||||
* @return SSH_OK on success, SSH_ERROR on error.
|
||||
*
|
||||
|
@ -58,6 +58,7 @@
|
||||
#include "libssh/dh.h"
|
||||
#include "libssh/messages.h"
|
||||
#include "libssh/options.h"
|
||||
#include "libssh/curve25519.h"
|
||||
|
||||
#define set_status(session, status) do {\
|
||||
if (session->common.callbacks && session->common.callbacks->connect_status_function) \
|
||||
@ -182,6 +183,11 @@ SSH_PACKET_CALLBACK(ssh_packet_kexdh_init){
|
||||
case SSH_KEX_ECDH_SHA2_NISTP256:
|
||||
rc = ssh_server_ecdh_init(session, packet);
|
||||
break;
|
||||
#endif
|
||||
#ifdef HAVE_CURVE25519
|
||||
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
|
||||
rc = ssh_server_curve25519_init(session, packet);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ssh_set_error(session,SSH_FATAL,"Wrong kex type in ssh_packet_kexdh_init");
|
||||
|
@ -115,6 +115,17 @@ ssh_session ssh_new(void) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
#ifdef HAVE_ECC
|
||||
id = strdup("%d/id_ecdsa");
|
||||
if (id == NULL) {
|
||||
goto err;
|
||||
}
|
||||
rc = ssh_list_append(session->opts.identity, id);
|
||||
if (rc == SSH_ERROR) {
|
||||
goto err;
|
||||
}
|
||||
#endif
|
||||
|
||||
id = strdup("%d/id_rsa");
|
||||
if (id == NULL) {
|
||||
goto err;
|
||||
@ -268,7 +279,10 @@ void ssh_free(ssh_session session) {
|
||||
|
||||
/**
|
||||
* @brief get the server banner
|
||||
*
|
||||
* @param[in] session The SSH session
|
||||
*
|
||||
* @return Returns the server banner string or NULL.
|
||||
*/
|
||||
const char* ssh_get_serverbanner(ssh_session session) {
|
||||
if(!session) {
|
||||
@ -300,8 +314,6 @@ void ssh_silent_disconnect(ssh_session session) {
|
||||
* @param[in] session The ssh session to change.
|
||||
*
|
||||
* @param[in] blocking Zero for nonblocking mode.
|
||||
*
|
||||
* \bug nonblocking code is in development and won't work as expected
|
||||
*/
|
||||
void ssh_set_blocking(ssh_session session, int blocking) {
|
||||
if (session == NULL) {
|
||||
@ -577,7 +589,7 @@ int ssh_get_status(ssh_session session) {
|
||||
|
||||
socketstate = ssh_socket_get_status(session->socket);
|
||||
|
||||
if (session->closed) {
|
||||
if (session->session_state == SSH_SESSION_STATE_DISCONNECTED) {
|
||||
r |= SSH_CLOSED;
|
||||
}
|
||||
if (socketstate & SSH_READ_PENDING) {
|
||||
@ -586,7 +598,8 @@ int ssh_get_status(ssh_session session) {
|
||||
if (socketstate & SSH_WRITE_PENDING) {
|
||||
r |= SSH_WRITE_PENDING;
|
||||
}
|
||||
if ((session->closed && (socketstate & SSH_CLOSED_ERROR)) ||
|
||||
if ((session->session_state == SSH_SESSION_STATE_DISCONNECTED &&
|
||||
(socketstate & SSH_CLOSED_ERROR)) ||
|
||||
session->session_state == SSH_SESSION_STATE_ERROR) {
|
||||
r |= SSH_CLOSED_ERROR;
|
||||
}
|
||||
@ -610,12 +623,9 @@ const char *ssh_get_disconnect_message(ssh_session session) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!session->closed) {
|
||||
if (session->session_state != SSH_SESSION_STATE_DISCONNECTED) {
|
||||
ssh_set_error(session, SSH_REQUEST_DENIED,
|
||||
"Connection not closed yet");
|
||||
} else if(session->closed_by_except) {
|
||||
ssh_set_error(session, SSH_REQUEST_DENIED,
|
||||
"Connection closed by socket error");
|
||||
} else if(!session->discon_msg) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Connection correctly closed but no disconnect message");
|
||||
|
Loading…
Reference in New Issue
Block a user