summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIru Cai <mytbk920423@gmail.com>2018-05-10 21:17:47 +0800
committerIru Cai <mytbk920423@gmail.com>2018-05-10 21:19:27 +0800
commitae53eddc611459fa427db9c65f6213d3e817aa31 (patch)
tree51b5cdf33dbb3abb5ace6fa766b21110dc2f99eb
parent4937c80cb0b5747fe5983d24c83fc61c796b6882 (diff)
downloadfqterm-ae53eddc611459fa427db9c65f6213d3e817aa31.tar.xz
restruct SSH RSA verification
- put I_C, I_S, V_C, V_S, K_S to ssh_session - move RSA verification code to C code
-rw-r--r--src/protocol/CMakeLists.txt1
-rw-r--r--src/protocol/internal/fqterm_ssh2_kex.cpp202
-rw-r--r--src/protocol/internal/fqterm_ssh2_kex.h9
-rw-r--r--src/protocol/internal/ssh_rsa.c127
-rw-r--r--src/protocol/internal/ssh_rsa.h20
-rw-r--r--src/protocol/internal/ssh_session.h5
6 files changed, 212 insertions, 152 deletions
diff --git a/src/protocol/CMakeLists.txt b/src/protocol/CMakeLists.txt
index 5c98fa7..5c35577 100644
--- a/src/protocol/CMakeLists.txt
+++ b/src/protocol/CMakeLists.txt
@@ -14,6 +14,7 @@ set(export_SRCS
set(internal_SRCS
internal/buffer.c
+ internal/ssh_rsa.c
internal/ssh_crypto_common.h
internal/ssh_crypto_common.c
internal/ssh_cipher.h
diff --git a/src/protocol/internal/fqterm_ssh2_kex.cpp b/src/protocol/internal/fqterm_ssh2_kex.cpp
index b0eb7c2..d1c6b7d 100644
--- a/src/protocol/internal/fqterm_ssh2_kex.cpp
+++ b/src/protocol/internal/fqterm_ssh2_kex.cpp
@@ -30,31 +30,37 @@
#include "fqterm_trace.h"
#include "ssh_pubkey_crypto.h"
#include "ssh_cipher.h"
+#include "ssh_rsa.h"
namespace FQTerm {
-FQTermSSH2Kex::FQTermSSH2Kex(const char *V_C, const char *V_S)
- : FQTermSSHKex(V_C, V_S) {
- is_first_kex_ = true;
- kex_state_ = FQTermSSH2Kex::BEFORE_KEXINIT;
+FQTermSSH2Kex::FQTermSSH2Kex(const char *V_C, const char *V_S)
+ : FQTermSSHKex(V_C, V_S)
+{
+ is_first_kex_ = true;
+ kex_state_ = FQTermSSH2Kex::BEFORE_KEXINIT;
+
+ sess.I_C = NULL;
+ sess.I_S = NULL;
+ sess.K_S = NULL;
+ sess.session_id = NULL;
+ sess.V_C = this->V_C_;
+ sess.V_S = this->V_S_;
+}
- I_C_len_ = 0;
- I_C_ = NULL;
- I_S_len_ = 0;
- I_S_ = NULL;
- K_S_ = NULL;
+FQTermSSH2Kex::~FQTermSSH2Kex()
+{
+ if (sess.I_C)
+ delete[] sess.I_C;
- sess.session_id = NULL;
-}
+ if (sess.I_S)
+ delete[] sess.I_S;
-FQTermSSH2Kex::~FQTermSSH2Kex() {
- delete[] I_C_;
- delete[] I_S_;
- if (K_S_)
- delete [] K_S_;
+ if (sess.K_S)
+ free(sess.K_S);
- if (sess.session_id != NULL)
- delete[] sess.session_id;
+ if (sess.session_id != NULL)
+ delete[] sess.session_id;
}
void FQTermSSH2Kex::initKex(FQTermSSHPacketReceiver *packetReceiver,
@@ -124,11 +130,12 @@ bool FQTermSSH2Kex::negotiateAlgorithms() {
}
// 0. Backup the payload of this server packet.
- I_S_len_ = packet_receiver_->packetDataLen() + 1; // add 1 bytes for packet type.
- delete[] I_S_;
- I_S_ = new char[I_S_len_];
- I_S_[0] = SSH2_MSG_KEXINIT;
- memcpy(I_S_ + 1, buffer_data(&packet_receiver_->recvbuf), I_S_len_ - 1);
+ sess.I_S_len = packet_receiver_->packetDataLen() + 1; // add 1 bytes for packet type.
+ if (sess.I_S)
+ delete[] sess.I_S;
+ sess.I_S = new char[sess.I_S_len];
+ sess.I_S[0] = SSH2_MSG_KEXINIT;
+ memcpy(sess.I_S + 1, buffer_data(&packet_receiver_->recvbuf), sess.I_S_len - 1);
// 1. Parse server kex init packet
packet_receiver_->getRawData((char*)cookie_, 16);
@@ -234,10 +241,11 @@ bool FQTermSSH2Kex::negotiateAlgorithms() {
packet_sender_->putInt(0);
// 3. backup the payload of this client packet.
- I_C_len_ = buffer_len(&packet_sender_->orig_data);
- delete[] I_C_;
- I_C_ = new char[I_C_len_];
- memcpy(I_C_, buffer_data(&packet_sender_->orig_data), I_C_len_);
+ sess.I_C_len = buffer_len(&packet_sender_->orig_data);
+ if (sess.I_C)
+ delete [] sess.I_C;
+ sess.I_C = new char[sess.I_C_len];
+ memcpy(sess.I_C, buffer_data(&packet_sender_->orig_data), sess.I_C_len);
// 4. send packet to server
packet_sender_->write();
@@ -266,124 +274,30 @@ void FQTermSSH2Kex::exchangeKey()
packet_sender_->write();
}
-static RSA *CreateRSAContext(unsigned char *host_key, int len);
-
-bool FQTermSSH2Kex::verifyKey() {
- if (packet_receiver_->packetType() != SSH2_MSG_KEXDH_REPLY) {
- emit kexError(tr("Expect a SSH_MSG_KEXDH_REPLY packet"));
- return false;
- }
-
-
- if (K_S_)
- delete [] K_S_;
- K_S_ = (char*)packet_receiver_->getString(&K_S_len_);
-
- int mpint_f_len;
- unsigned char *mpint_f = (unsigned char *)packet_receiver_->getString(&mpint_f_len);
- if (ssh_dh_compute_secret(sess.dh, mpint_f, mpint_f_len) < 0) {
- emit kexError(tr("Error when computing shared secret"));
- delete mpint_f;
- return false;
- }
-
- int s_len = -1;
- unsigned char *s = (unsigned char *)packet_receiver_->getString(&s_len);
-
- buffer vbuf;
- buffer_init(&vbuf);
- buffer_append_string(&vbuf, V_C_, strlen(V_C_));
- buffer_append_string(&vbuf, V_S_, strlen(V_S_));
- buffer_append_string(&vbuf, I_C_, I_C_len_);
- buffer_append_string(&vbuf, I_S_, I_S_len_);
- buffer_append_string(&vbuf, K_S_, K_S_len_);
- buffer_append(&vbuf, sess.dh->mpint_e, sess.dh->e_len);
- buffer_append_string(&vbuf, (const char*)mpint_f, mpint_f_len);
- buffer_append(&vbuf, sess.dh->secret, sess.dh->secret_len);
-
- ssh_dh_hash(sess.dh, buffer_data(&vbuf), sess.H, buffer_len(&vbuf));
-
- buffer_deinit(&vbuf);
-
- // Start verify
- // ssh-rsa specifies SHA-1 hashing
- unsigned char s_H[SHA_DIGEST_LENGTH];
- SHA1(sess.H, sess.dh->digest.hashlen, s_H);
-
- // Ignore the first 15 bytes of the signature of H sent from server:
- // algorithm_name_length[4], algorithm_name[7]("ssh-rsa") and signature_length[4].
- RSA *rsactx = CreateRSAContext((unsigned char*)K_S_, K_S_len_);
- if (rsactx == NULL) {
- emit kexError("Fail to get the RSA key!");
- return false;
- }
- int sig_len = s_len - 15;
- unsigned char *sig = s + 15;
- int res = RSA_verify(NID_sha1, s_H, SHA_DIGEST_LENGTH,
- sig, sig_len, rsactx);
-
- RSA_free(rsactx);
-
- delete [] mpint_f;
- delete [] s;
-
- return res == 1;
-}
-
-static RSA *CreateRSAContext(unsigned char *hostkey, int len)
+bool FQTermSSH2Kex::verifyKey()
{
- int algo_len, e_len, n_len;
- RSA *rsa = RSA_new();
- BIGNUM *rsa_e = BN_new();
- BIGNUM *rsa_n = BN_new();
-
- if (len >= 4)
- algo_len = be32toh(*(uint32_t*)hostkey);
- else
- goto fail;
- hostkey += 4;
- len -= 4;
-
- if (!(len >= 7 && algo_len == 7 && memcmp(hostkey, "ssh-rsa", 7) == 0))
- goto fail;
- hostkey += 7;
- len -= 7;
-
- if (len >= 4)
- e_len = be32toh(*(uint32_t*)hostkey);
- else
- goto fail;
- if (len >= 4+e_len)
- BN_mpi2bn(hostkey, 4+e_len, rsa_e);
- else
- goto fail;
- hostkey += 4 + e_len;
- len -= 4 + e_len;
-
- if (len >= 4)
- n_len = be32toh(*(uint32_t*)hostkey);
- else
- goto fail;
- if (len >= 4+n_len)
- BN_mpi2bn(hostkey, 4+n_len, rsa_n);
- else
- goto fail;
- hostkey += 4 + n_len;
- len -= 4 + n_len;
-
-#ifdef HAVE_OPAQUE_STRUCTS
- RSA_set0_key(rsa, rsa_n, rsa_e, NULL);
-#else
- rsa->n = rsa_n;
- rsa->e = rsa_e;
-#endif
-
- return rsa;
-fail:
- BN_clear_free(rsa_e);
- BN_clear_free(rsa_n);
- RSA_free(rsa);
- return NULL;
+ if (packet_receiver_->packetType() != SSH2_MSG_KEXDH_REPLY) {
+ emit kexError(tr("Expect a SSH_MSG_KEXDH_REPLY packet"));
+ return false;
+ }
+
+ buffer *buf = &packet_receiver_->recvbuf;
+ int res = verifyRSAKey(&sess, buffer_data(buf), buffer_len(buf));
+ switch (res) {
+ case 1:
+ return true;
+ case 0:
+ return false;
+ case -ESECRET:
+ emit kexError(tr("Error computing secret!"));
+ return false;
+ case -ERSA:
+ emit kexError(tr("Fail to get the RSA key!"));
+ return false;
+ default:
+ emit kexError(tr("verifyKey: unknown error!"));
+ return false;
+ }
}
void FQTermSSH2Kex::sendNewKeys(){
diff --git a/src/protocol/internal/fqterm_ssh2_kex.h b/src/protocol/internal/fqterm_ssh2_kex.h
index 978cb17..d1ce7b3 100644
--- a/src/protocol/internal/fqterm_ssh2_kex.h
+++ b/src/protocol/internal/fqterm_ssh2_kex.h
@@ -51,13 +51,6 @@ private:
mpint K, the shared secret
*/
- int I_C_len_;
- char *I_C_;
- int I_S_len_;
- char *I_S_;
- int K_S_len_;
- char *K_S_;
-
ssh_session sess;
bool is_first_kex_;
@@ -81,7 +74,7 @@ public:
FQTermSSHPacketSender *outputSender);
void hostKeyHash(unsigned char *md)
{
- SHA256((const unsigned char*)K_S_, K_S_len_, md);
+ SHA256(sess.K_S, sess.K_S_len, md);
}
public slots:
diff --git a/src/protocol/internal/ssh_rsa.c b/src/protocol/internal/ssh_rsa.c
new file mode 100644
index 0000000..2bd328c
--- /dev/null
+++ b/src/protocol/internal/ssh_rsa.c
@@ -0,0 +1,127 @@
+#include <string.h>
+#include "ssh_pubkey_crypto.h"
+#include "ssh_rsa.h"
+#include "buffer.h"
+
+static RSA *CreateRSAContext(unsigned char *hostkey, int len)
+{
+ int algo_len, e_len, n_len;
+ RSA *rsa = RSA_new();
+ BIGNUM *rsa_e = BN_new();
+ BIGNUM *rsa_n = BN_new();
+
+ if (len >= 4)
+ algo_len = be32toh(*(uint32_t *)hostkey);
+ else
+ goto fail;
+ hostkey += 4;
+ len -= 4;
+
+ if (!(len >= 7 && algo_len == 7 && memcmp(hostkey, "ssh-rsa", 7) == 0))
+ goto fail;
+ hostkey += 7;
+ len -= 7;
+
+ if (len >= 4)
+ e_len = be32toh(*(uint32_t *)hostkey);
+ else
+ goto fail;
+ if (len >= 4 + e_len)
+ BN_mpi2bn(hostkey, 4 + e_len, rsa_e);
+ else
+ goto fail;
+ hostkey += 4 + e_len;
+ len -= 4 + e_len;
+
+ if (len >= 4)
+ n_len = be32toh(*(uint32_t *)hostkey);
+ else
+ goto fail;
+ if (len >= 4 + n_len)
+ BN_mpi2bn(hostkey, 4 + n_len, rsa_n);
+ else
+ goto fail;
+ hostkey += 4 + n_len;
+ len -= 4 + n_len;
+
+#ifdef HAVE_OPAQUE_STRUCTS
+ RSA_set0_key(rsa, rsa_n, rsa_e, NULL);
+#else
+ rsa->n = rsa_n;
+ rsa->e = rsa_e;
+#endif
+
+ return rsa;
+fail:
+ BN_clear_free(rsa_e);
+ BN_clear_free(rsa_n);
+ RSA_free(rsa);
+ return NULL;
+}
+
+static inline const uint8_t *get_str(const uint8_t **data, uint32_t *L)
+{
+ const uint8_t *ptr = *data;
+ uint32_t len = be32toh(*(uint32_t *)ptr);
+ *data = ptr + 4 + len;
+ *L = len;
+ return ptr + 4;
+}
+
+int verifyRSAKey(ssh_session *ss, const uint8_t *data, size_t len)
+{
+ const uint8_t *next = data;
+
+ uint32_t K_S_len;
+ const uint8_t *K_S;
+ K_S = get_str(&next, &K_S_len);
+ ss->K_S_len = K_S_len;
+ ss->K_S = (uint8_t *)malloc(K_S_len);
+ memcpy(ss->K_S, K_S, K_S_len);
+
+ uint32_t mpint_f_len;
+ const uint8_t *mpint_f;
+ mpint_f = get_str(&next, &mpint_f_len);
+
+ if (ssh_dh_compute_secret(ss->dh, mpint_f, mpint_f_len) < 0)
+ return -ESECRET;
+
+ uint32_t s_len;
+ const uint8_t *s;
+ s = get_str(&next, &s_len);
+
+ buffer vbuf;
+ buffer_init(&vbuf);
+ buffer_append_string(&vbuf, ss->V_C, strlen(ss->V_C));
+ buffer_append_string(&vbuf, ss->V_S, strlen(ss->V_S));
+ buffer_append_string(&vbuf, ss->I_C, ss->I_C_len);
+ buffer_append_string(&vbuf, ss->I_S, ss->I_S_len);
+ buffer_append_string(&vbuf, (const char *)K_S, K_S_len);
+ buffer_append(&vbuf, ss->dh->mpint_e, ss->dh->e_len);
+ buffer_append_string(&vbuf, (const char *)mpint_f, mpint_f_len);
+ buffer_append(&vbuf, ss->dh->secret, ss->dh->secret_len);
+
+ ssh_dh_hash(ss->dh, buffer_data(&vbuf), ss->H, buffer_len(&vbuf));
+
+ buffer_deinit(&vbuf);
+
+ // Start verify
+ // ssh-rsa specifies SHA-1 hashing
+ unsigned char s_H[SHA_DIGEST_LENGTH];
+ SHA1(ss->H, ss->dh->digest.hashlen, s_H);
+
+ // Ignore the first 15 bytes of the signature of H sent from server:
+ // algorithm_name_length[4], algorithm_name[7]("ssh-rsa") and signature_length[4].
+ RSA *rsactx = CreateRSAContext((unsigned char *)K_S, K_S_len);
+ if (rsactx == NULL)
+ return -ERSA;
+
+ int sig_len = s_len - 15;
+ const uint8_t *sig = s + 15;
+ int res = RSA_verify(NID_sha1, s_H, SHA_DIGEST_LENGTH, sig, sig_len,
+ rsactx);
+
+ RSA_free(rsactx);
+
+ return (res == 1);
+}
diff --git a/src/protocol/internal/ssh_rsa.h b/src/protocol/internal/ssh_rsa.h
new file mode 100644
index 0000000..bccce00
--- /dev/null
+++ b/src/protocol/internal/ssh_rsa.h
@@ -0,0 +1,20 @@
+#ifndef SSH_RSA
+#define SSH_RSA
+
+#include "ssh_session.h"
+#include <stdlib.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* } */
+
+enum { ESECRET = 1, ERSA };
+
+int verifyRSAKey(ssh_session *ss, const uint8_t *data, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/protocol/internal/ssh_session.h b/src/protocol/internal/ssh_session.h
index d7df1c9..98adb50 100644
--- a/src/protocol/internal/ssh_session.h
+++ b/src/protocol/internal/ssh_session.h
@@ -12,6 +12,11 @@ typedef struct
unsigned char *session_id;
unsigned char H[SHA512_DIGEST_LENGTH];
SSH_DH *dh;
+
+ const char *V_C, *V_S;
+ size_t I_C_len, I_S_len, K_S_len;
+ char *I_C, *I_S;
+ uint8_t *K_S;
} ssh_session;
void computeKey(ssh_session *, int, char, unsigned char []);