From 88fed792b68835ea7805bcfed6762f0ee9496bab Mon Sep 17 00:00:00 2001 From: Iru Cai Date: Tue, 8 Nov 2016 14:59:15 +0800 Subject: better handling of errors when KEX fails, use EVP digest --- src/protocol/internal/fqterm_ssh2_kex.cpp | 130 ++++++++++++++++------------- src/protocol/internal/fqterm_ssh2_kex.h | 7 +- src/protocol/internal/ssh_diffie-hellman.c | 23 ++++- src/protocol/internal/ssh_diffie-hellman.h | 20 ++++- 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 #include +#include + +#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[]; -- cgit v1.2.3