summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIru Cai <mytbk920423@gmail.com>2016-11-08 14:59:15 +0800
committerIru Cai <mytbk920423@gmail.com>2016-11-08 15:17:41 +0800
commit88fed792b68835ea7805bcfed6762f0ee9496bab (patch)
treea1c1abfe0afc3759be0acedd0313bee1fcfcf4fb
parent50d144a7305c6f454a8f8f1a10ac7234eac2ceeb (diff)
downloadfqterm-88fed792b68835ea7805bcfed6762f0ee9496bab.tar.xz
better handling of errors when KEX fails, use EVP digest
-rw-r--r--src/protocol/internal/fqterm_ssh2_kex.cpp130
-rw-r--r--src/protocol/internal/fqterm_ssh2_kex.h7
-rw-r--r--src/protocol/internal/ssh_diffie-hellman.c23
-rw-r--r--src/protocol/internal/ssh_diffie-hellman.h20
4 files changed, 109 insertions, 71 deletions
diff --git a/src/protocol/internal/fqterm_ssh2_kex.cpp b/src/protocol/internal/fqterm_ssh2_kex.cpp
index 59ec85e..fe35452 100644
--- a/src/protocol/internal/fqterm_ssh2_kex.cpp
+++ b/src/protocol/internal/fqterm_ssh2_kex.cpp
@@ -60,7 +60,6 @@ FQTermSSH2Kex::~FQTermSSH2Kex() {
BN_clear_free(bn_x_);
BN_clear_free(bn_e_);
- ssh_dh_free(dh);
BN_CTX_free(ctx_);
BN_clear_free(bn_K_);
@@ -80,39 +79,41 @@ void FQTermSSH2Kex::initKex(FQTermSSHPacketReceiver *packetReceiver,
emit reKex();
}
-void FQTermSSH2Kex::handlePacket(int type) {
- switch (kex_state_) {
- case FQTermSSH2Kex::BEFORE_KEXINIT:
- negotiateAlgorithms();
- exchangeKey();
- kex_state_ = FQTermSSH2Kex::WAIT_REPLY;
- break;
- case FQTermSSH2Kex::WAIT_REPLY:
- if (verifyKey()) {
- sendNewKeys();
- kex_state_ = FQTermSSH2Kex::SESSIONKEY_SENT;
- } else {
- emit kexError(tr("Key exchange failed!"));
- }
- break;
- case FQTermSSH2Kex::SESSIONKEY_SENT:
- if (changeKeyAlg()) {
- kex_state_ = FQTermSSH2Kex::KEYEX_OK;
- emit kexOK();
- }
- break;
- case FQTermSSH2Kex::KEYEX_OK:
- // TODO: how about Key Re-Exchange (see RFC 4253, 9. Key Re-Exchange)
- break;
- }
+void FQTermSSH2Kex::handlePacket(int type)
+{
+ switch (kex_state_) {
+ case FQTermSSH2Kex::BEFORE_KEXINIT:
+ if (!negotiateAlgorithms())
+ return;
+ exchangeKey();
+ kex_state_ = FQTermSSH2Kex::WAIT_REPLY;
+ break;
+ case FQTermSSH2Kex::WAIT_REPLY:
+ if (verifyKey()) {
+ sendNewKeys();
+ kex_state_ = FQTermSSH2Kex::SESSIONKEY_SENT;
+ } else {
+ emit kexError(tr("Key exchange failed!"));
+ }
+ break;
+ case FQTermSSH2Kex::SESSIONKEY_SENT:
+ if (changeKeyAlg()) {
+ kex_state_ = FQTermSSH2Kex::KEYEX_OK;
+ emit kexOK();
+ }
+ break;
+ case FQTermSSH2Kex::KEYEX_OK:
+ // TODO: how about Key Re-Exchange (see RFC 4253, 9. Key Re-Exchange)
+ break;
+ }
}
-void FQTermSSH2Kex::negotiateAlgorithms() {
+bool FQTermSSH2Kex::negotiateAlgorithms() {
FQ_FUNC_TRACE("ssh2kex", 10);
-
+
if (packet_receiver_->packetType() != SSH2_MSG_KEXINIT) {
emit kexError(tr("startKex: First packet is not SSH_MSG_KEXINIT"));
- return ;
+ return false;
}
// 0. Backup the payload of this server packet.
@@ -130,12 +131,12 @@ void FQTermSSH2Kex::negotiateAlgorithms() {
char kex_algos[kl_len+1];
packet_receiver_->getRawData(kex_algos, kl_len);
kex_algos[kl_len] = '\0';
- NEW_DH dh = search_dh(kex_algos);
- if (dh==NULL) {
+ NEW_DH new_dh = search_dh(kex_algos);
+ if (new_dh==NULL) {
emit kexError(tr("No matching KEX algorithms!"));
- return;
+ return false;
}
- this->dh = dh();
+ this->dh = new_dh();
// TODO: host key algorithms
size_t hk_algo_len = packet_receiver_->getInt();
@@ -151,7 +152,7 @@ void FQTermSSH2Kex::negotiateAlgorithms() {
NEW_CIPHER c2s = search_cipher(el_c2s);
if (c2s==NULL) {
emit kexError(tr("No matching c2s cipher algorithms!"));
- return;
+ return false;
}
packet_sender_->cipher = c2s(1);
@@ -163,7 +164,7 @@ void FQTermSSH2Kex::negotiateAlgorithms() {
NEW_CIPHER s2c = search_cipher(el_s2c);
if (s2c==NULL) {
emit kexError(tr("No matching s2c cipher algorithms!"));
- return;
+ return false;
}
packet_receiver_->cipher = s2c(0);
@@ -209,6 +210,8 @@ void FQTermSSH2Kex::negotiateAlgorithms() {
// 4. send packet to server
packet_sender_->write();
+
+ return true;
}
void FQTermSSH2Kex::exchangeKey() {
@@ -252,11 +255,12 @@ bool FQTermSSH2Kex::verifyKey() {
buffer->putSSH2BN(bn_f_);
buffer->putSSH2BN(bn_K_);
- dh->hash(buffer->data(), buffer->len(), H_);
+ ssh_dh_hash(dh, buffer->data(), H_, buffer->len());
// Start verify
+ // ssh-rsa specifies SHA-1 hashing
unsigned char s_H[SHA_DIGEST_LENGTH];
- SHA1(H_, dh->hashlen, s_H);
+ SHA1(H_, 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].
@@ -323,8 +327,8 @@ bool FQTermSSH2Kex::changeKeyAlg() {
}
if (session_id_ == NULL) {
- session_id_ = new unsigned char[SHA_DIGEST_LENGTH];
- memcpy(session_id_, H_, SHA_DIGEST_LENGTH);
+ session_id_ = new unsigned char[dh->digest.hashlen];
+ memcpy(session_id_, H_, dh->digest.hashlen);
}
packet_sender_->setMacType(FQTERM_SSH_HMAC_SHA1);
@@ -370,6 +374,8 @@ bool FQTermSSH2Kex::changeKeyAlg() {
packet_receiver_->startEncryption(key_s2c, IV_s2c);
packet_receiver_->startMac(mac_key_s2c);
+ /* now key exchange ends */
+ ssh_dh_free(dh);
delete[] IV_c2s;
delete[] IV_s2c;
@@ -381,32 +387,36 @@ bool FQTermSSH2Kex::changeKeyAlg() {
return true;
}
-unsigned char *FQTermSSH2Kex::computeKey(int expected_len, char flag) {
- unsigned char *key = new unsigned char[expected_len + SHA_DIGEST_LENGTH];
+unsigned char *FQTermSSH2Kex::computeKey(int expected_len, char flag)
+{
+ unsigned char *key = new unsigned char[expected_len + SHA_DIGEST_LENGTH];
- int len = 0;
- SHA_CTX hash;
+ int len = 0;
- FQTermSSHBuffer K(BN_num_bytes(bn_K_) + 5);
- K.putSSH2BN(bn_K_);
+ EVP_MD_CTX *mdctx = dh->digest.mdctx;
+ const EVP_MD *md = dh->digest.md;
+ int hashlen = dh->digest.hashlen;
- while (len < expected_len) {
- SHA1_Init(&hash);
- SHA1_Update(&hash, K.data(), K.len());
- SHA1_Update(&hash, H_, SHA_DIGEST_LENGTH);
+ FQTermSSHBuffer K(BN_num_bytes(bn_K_) + 5);
+ K.putSSH2BN(bn_K_);
- if (len == 0) {
- SHA1_Update(&hash, &flag, 1);
- SHA1_Update(&hash, session_id_, SHA_DIGEST_LENGTH);
- } else {
- SHA1_Update(&hash, key, len);
- }
-
- SHA1_Final(key + len, &hash);
- len += SHA_DIGEST_LENGTH;
- }
+ while (len < expected_len) {
+ EVP_DigestInit_ex(mdctx, md, NULL);
+ EVP_DigestUpdate(mdctx, K.data(), K.len());
+ EVP_DigestUpdate(mdctx, H_, hashlen);
+
+ if (len == 0) {
+ EVP_DigestUpdate(mdctx, &flag, 1);
+ EVP_DigestUpdate(mdctx, session_id_, hashlen);
+ } else {
+ EVP_DigestUpdate(mdctx, key, len);
+ }
+
+ EVP_DigestFinal_ex(mdctx, key+len, NULL);
+ len += SHA_DIGEST_LENGTH;
+ }
- return key;
+ return key;
}
} // namespace FQTerm
diff --git a/src/protocol/internal/fqterm_ssh2_kex.h b/src/protocol/internal/fqterm_ssh2_kex.h
index 59e3330..0a09cc1 100644
--- a/src/protocol/internal/fqterm_ssh2_kex.h
+++ b/src/protocol/internal/fqterm_ssh2_kex.h
@@ -68,9 +68,6 @@ private:
unsigned char *session_id_;
-
-
-
bool is_first_kex_;
ssh_pubkey_t *host_key_;
@@ -80,8 +77,8 @@ private:
int server_flag_, ciphers_, auth_;
// u_char session_id_[16];
u_char session_key_[32];
-
- void negotiateAlgorithms();
+
+ bool negotiateAlgorithms();
void exchangeKey();
bool verifyKey();
void sendNewKeys();
diff --git a/src/protocol/internal/ssh_diffie-hellman.c b/src/protocol/internal/ssh_diffie-hellman.c
index 1866b2f..72bd253 100644
--- a/src/protocol/internal/ssh_diffie-hellman.c
+++ b/src/protocol/internal/ssh_diffie-hellman.c
@@ -63,6 +63,7 @@ ssh_dh_free(SSH_DH *dh)
{
BN_free(dh->g);
BN_free(dh->p);
+ ssh_md_ctx_free(dh->digest.mdctx);
free(dh);
}
@@ -72,8 +73,11 @@ ssh_dh_group1_sha1(void)
SSH_DH *dh = (SSH_DH*)malloc(sizeof(SSH_DH));
dh->g = BN_new();
dh->p = BN_new();
- dh->hash = SHA1;
- dh->hashlen = SHA_DIGEST_LENGTH;
+ dh->digest = (evp_md_t) {
+ .mdctx = ssh_md_ctx_new(),
+ .md = EVP_sha1(),
+ .hashlen = SHA_DIGEST_LENGTH
+ };
BN_set_word(dh->g, g);
BN_bin2bn(prime_group1, 128, dh->p);
return dh;
@@ -85,13 +89,24 @@ ssh_dh_group14_sha1(void)
SSH_DH *dh = (SSH_DH*)malloc(sizeof(SSH_DH));
dh->g = BN_new();
dh->p = BN_new();
- dh->hash = SHA1;
- dh->hashlen = SHA_DIGEST_LENGTH;
+ dh->digest = (evp_md_t) {
+ .mdctx = ssh_md_ctx_new(),
+ .md = EVP_sha1(),
+ .hashlen = SHA_DIGEST_LENGTH
+ };
BN_set_word(dh->g, g);
BN_bin2bn(prime_group14, 256, dh->p);
return dh;
}
+void
+ssh_dh_hash(SSH_DH *dh, const unsigned char *in, unsigned char *out, size_t n)
+{
+ EVP_DigestInit_ex(dh->digest.mdctx, dh->digest.md, NULL);
+ EVP_DigestUpdate(dh->digest.mdctx, in, n);
+ EVP_DigestFinal_ex(dh->digest.mdctx, out, NULL);
+}
+
struct
{
const char *name;
diff --git a/src/protocol/internal/ssh_diffie-hellman.h b/src/protocol/internal/ssh_diffie-hellman.h
index 5468fdb..f132493 100644
--- a/src/protocol/internal/ssh_diffie-hellman.h
+++ b/src/protocol/internal/ssh_diffie-hellman.h
@@ -3,6 +3,15 @@
#include <openssl/bn.h>
#include <openssl/sha.h>
+#include <openssl/evp.h>
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+#define ssh_md_ctx_new EVP_MD_CTX_new
+#define ssh_md_ctx_free EVP_MD_CTX_free
+#else
+#define ssh_md_ctx_new EVP_MD_CTX_create
+#define ssh_md_ctx_free EVP_MD_CTX_destroy
+#endif
#ifdef __cplusplus
extern "C" {
@@ -10,17 +19,24 @@ extern "C" {
typedef unsigned char* (*hash_t)(const unsigned char *, size_t, unsigned char *);
+ typedef struct
+ {
+ EVP_MD_CTX *mdctx;
+ const EVP_MD *md;
+ unsigned int hashlen;
+ } evp_md_t;
+
typedef struct ssh_diffie_hellman
{
BIGNUM *g; // generator
BIGNUM *p; // prime
- hash_t hash; // can be SHA1 or SHA256
- size_t hashlen;
+ evp_md_t digest;
} SSH_DH;
void ssh_dh_free(SSH_DH*);
SSH_DH *ssh_dh_group1_sha1(void);
SSH_DH *ssh_dh_group14_sha1(void);
+ void ssh_dh_hash(SSH_DH*, const unsigned char* data, unsigned char*, size_t len);
typedef SSH_DH*(*NEW_DH)(void);
extern const char all_dh_list[];