diff options
author | Aris Adamantiadis <aris@0xbadc0de.be> | 2013-09-21 23:34:50 +0200 |
---|---|---|
committer | Aris Adamantiadis <aris@0xbadc0de.be> | 2013-09-27 15:32:44 +0200 |
commit | 4cb6afcbd43ab503d4c3d3054b96a1492605ea8d (patch) | |
tree | dd142bcab88629a984936e708cca3d5ac9a1d1c6 /src/curve25519.c | |
parent | 4eae4d592cb9195cac49832bf3bd4052c418b948 (diff) | |
download | libssh-4cb6afcbd43ab503d4c3d3054b96a1492605ea8d.tar.gz libssh-4cb6afcbd43ab503d4c3d3054b96a1492605ea8d.tar.xz libssh-4cb6afcbd43ab503d4c3d3054b96a1492605ea8d.zip |
kex: implement curve25519-sha256@libssh.org
Diffstat (limited to 'src/curve25519.c')
-rw-r--r-- | src/curve25519.c | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/src/curve25519.c b/src/curve25519.c new file mode 100644 index 00000000..653beee0 --- /dev/null +++ b/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 */ |