From c527d1f2e16b6be7b299164bf5c0112932a8dd3c Mon Sep 17 00:00:00 2001 From: Iru Cai Date: Sat, 28 Apr 2018 17:46:22 +0800 Subject: refactor SSH MAC --- src/protocol/CMakeLists.txt | 4 +- src/protocol/internal/fqterm_ssh2_kex.cpp | 33 +++++++++-- src/protocol/internal/fqterm_ssh2_packet.cpp | 24 ++++---- src/protocol/internal/fqterm_ssh_mac.cpp | 53 ------------------ src/protocol/internal/fqterm_ssh_mac.h | 54 ------------------ src/protocol/internal/fqterm_ssh_packet.cpp | 39 +++---------- src/protocol/internal/fqterm_ssh_packet.h | 14 ++--- src/protocol/internal/ssh_mac.c | 82 ++++++++++++++++++++++++++++ src/protocol/internal/ssh_mac.h | 55 +++++++++++++++++++ 9 files changed, 190 insertions(+), 168 deletions(-) delete mode 100644 src/protocol/internal/fqterm_ssh_mac.cpp delete mode 100644 src/protocol/internal/fqterm_ssh_mac.h create mode 100644 src/protocol/internal/ssh_mac.c create mode 100644 src/protocol/internal/ssh_mac.h (limited to 'src/protocol') diff --git a/src/protocol/CMakeLists.txt b/src/protocol/CMakeLists.txt index b808fb9..84d53d1 100644 --- a/src/protocol/CMakeLists.txt +++ b/src/protocol/CMakeLists.txt @@ -19,6 +19,8 @@ set(internal_SRCS internal/all_ciphers.c internal/ssh_evp_cipher.c internal/ssh_3des-ssh1.c + internal/ssh_mac.h + internal/ssh_mac.c internal/ssh_pubkey_crypto.h internal/ssh_pubkey_crypto.c internal/ssh_diffie-hellman.h @@ -30,7 +32,6 @@ set(internal_SRCS internal/fqterm_ssh_const.h internal/fqterm_ssh_kex.h internal/fqterm_ssh2_kex.h - internal/fqterm_ssh_mac.h internal/fqterm_ssh_packet.h internal/fqterm_ssh1_packet.h internal/fqterm_ssh2_packet.h @@ -41,7 +42,6 @@ set(internal_SRCS internal/fqterm_ssh_buffer.cpp internal/fqterm_ssh_kex.cpp internal/fqterm_ssh2_kex.cpp - internal/fqterm_ssh_mac.cpp internal/fqterm_ssh_packet.cpp internal/fqterm_ssh1_packet.cpp internal/fqterm_ssh2_packet.cpp diff --git a/src/protocol/internal/fqterm_ssh2_kex.cpp b/src/protocol/internal/fqterm_ssh2_kex.cpp index 906acca..570c29f 100644 --- a/src/protocol/internal/fqterm_ssh2_kex.cpp +++ b/src/protocol/internal/fqterm_ssh2_kex.cpp @@ -167,8 +167,32 @@ bool FQTermSSH2Kex::negotiateAlgorithms() { } packet_receiver_->cipher = s2c(0); + // mac algo c2s + size_t m_c2s_len = packet_receiver_->getInt(); + char m_c2s[m_c2s_len+1]; + packet_receiver_->getRawData(m_c2s, m_c2s_len); + m_c2s[m_c2s_len] = '\0'; + const struct ssh_mac_t * mac_c2s = search_mac(m_c2s); + if (mac_c2s == NULL) { + emit kexError(tr("No matching c2s MAC algorithms!")); + return false; + } + packet_sender_->mac = mac_c2s->new_mac(mac_c2s); + + // mac algo s2c + size_t m_s2c_len = packet_receiver_->getInt(); + char m_s2c[m_s2c_len+1]; + packet_receiver_->getRawData(m_s2c, m_s2c_len); + m_s2c[m_s2c_len] = '\0'; + const struct ssh_mac_t * mac_s2c = search_mac(m_s2c); + if (mac_s2c == NULL) { + emit kexError(tr("No matching s2c MAC algorithms!")); + return false; + } + packet_receiver_->mac = mac_s2c->new_mac(mac_s2c); + std::vector name_lists; - for (int i = 4; i < 10; ++i) { + for (int i = 6; i < 10; ++i) { int name_lists_len = packet_receiver_->getInt(); if (name_lists_len > 0) { name_lists.resize(name_lists_len); @@ -191,8 +215,8 @@ bool FQTermSSH2Kex::negotiateAlgorithms() { packet_sender_->putString("ssh-rsa"); packet_sender_->putString(all_ciphers_list); packet_sender_->putString(all_ciphers_list); - packet_sender_->putString("hmac-sha1"); - packet_sender_->putString("hmac-sha1"); + packet_sender_->putString(all_macs_list); + packet_sender_->putString(all_macs_list); packet_sender_->putString("none"); packet_sender_->putString("none"); packet_sender_->putString(""); @@ -330,9 +354,6 @@ bool FQTermSSH2Kex::changeKeyAlg() { memcpy(session_id_, H_, dh->digest.hashlen); } - packet_sender_->setMacType(FQTERM_SSH_HMAC_SHA1); - packet_receiver_->setMacType(FQTERM_SSH_HMAC_SHA1); - // From RFC 4253 section 7.2: // Initial IV client to server: HASH(K || H || "A" || session_id) // (Here K is encoded as mpint and "A" as byte and session_id as raw diff --git a/src/protocol/internal/fqterm_ssh2_packet.cpp b/src/protocol/internal/fqterm_ssh2_packet.cpp index aed8b48..28c7847 100644 --- a/src/protocol/internal/fqterm_ssh2_packet.cpp +++ b/src/protocol/internal/fqterm_ssh2_packet.cpp @@ -62,9 +62,8 @@ void FQTermSSH2PacketSender::makePacket() { // 2. renew the output buffer. int total_len = non_padding_len + padding_len; - if (is_mac_) { - total_len += mac_->digestSize(); - } + if (is_mac_) + total_len += mac->dgstSize; delete output_buffer_; output_buffer_ = new FQTermSSHBuffer(total_len); @@ -95,8 +94,8 @@ void FQTermSSH2PacketSender::makePacket() { buffer.putInt(sequence_no_); buffer.putRawData((const char *)packet, len); - std::vector digest(mac_->digestSize()); - mac_->getDigest(buffer.data(), buffer.len(), &digest[0]); + std::vector digest(mac->dgstSize); + mac->getmac(mac, buffer.data(), buffer.len(), &digest[0]); FQ_TRACE("ssh2packet", 9) << "Making packets..."; FQ_TRACE("ssh2packet", 9) << "Append MAC with sequence_no_" << sequence_no_; @@ -121,7 +120,7 @@ void FQTermSSH2PacketSender::makePacket() { // with the given algorithm. u_char *data = output_buffer_->data(); - int len = output_buffer_->len() - mac_->digestSize(); + int len = output_buffer_->len() - mac->dgstSize; FQ_TRACE("ssh2packet", 9) << "An packet (without MAC) to be encrypted:" << len << " bytes:\n" @@ -174,7 +173,7 @@ void FQTermSSH2PacketReceiver::parseData(FQTermSSHBuffer *input) { return ; } - int expected_input_len = 4 + packet_len + (is_mac_ ? mac_->digestSize() : 0); + int expected_input_len = 4 + packet_len + (is_mac_ ? mac->dgstSize : 0); if (input->len() < (long)expected_input_len) { FQ_TRACE("ssh2packet", 3) @@ -189,15 +188,15 @@ void FQTermSSH2PacketReceiver::parseData(FQTermSSHBuffer *input) { if (is_decrypt_) { // decrypte blocks left. unsigned char *tmp = input->data() + cipher->blkSize; - int left_len = expected_input_len - cipher->blkSize - mac_->digestSize(); + int left_len = expected_input_len - cipher->blkSize - mac->dgstSize; FQ_VERIFY(cipher->crypt(cipher, tmp, tmp, left_len)==1); } // 3. check MAC if (is_mac_) { - int digest_len = mac_->digestSize(); + int digest_len = mac->dgstSize; std::vector digest(digest_len); - mac_->getDigest(input->data(), expected_input_len - digest_len, &digest[0]); + mac->getmac(mac, input->data(), expected_input_len - digest_len, &digest[0]); u_char *received_digest = input->data() + expected_input_len - digest_len; @@ -213,9 +212,8 @@ void FQTermSSH2PacketReceiver::parseData(FQTermSSHBuffer *input) { std::vector data(packet_len); input->getRawData((char*)&data[0], packet_len); - if (is_mac_) { - input->consume(mac_->digestSize()); - } + if (is_mac_) + input->consume(mac->dgstSize); int padding_len = data[0]; diff --git a/src/protocol/internal/fqterm_ssh_mac.cpp b/src/protocol/internal/fqterm_ssh_mac.cpp deleted file mode 100644 index e2d345f..0000000 --- a/src/protocol/internal/fqterm_ssh_mac.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/*************************************************************************** - * fqterm, a terminal emulator for both BBS and *nix. * - * Copyright (C) 2008 fqterm development group. * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program 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 General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * - ***************************************************************************/ - -#include -#include - -#include "fqterm_trace.h" -#include "fqterm_ssh_mac.h" -#include "fqterm_ssh_packet.h" - -namespace FQTerm { - -void FQTermSSHSHA1::setKey(const unsigned char *key) { - memcpy(key_, key, keySize()); -} - -int FQTermSSHSHA1::keySize() const { - return SHA_DIGEST_LENGTH; -} - -int FQTermSSHSHA1::digestSize() const { - return SHA_DIGEST_LENGTH; -} - -void FQTermSSHSHA1::getDigest(const unsigned char *data, int len, unsigned char *digest) const { - unsigned int tmp; - HMAC(EVP_sha1(), key_, SHA_DIGEST_LENGTH, data, len, digest, &tmp); - - FQ_TRACE("SHA1", 9) << "Key: \n" << dumpHexString << std::string((char *)key_, SHA_DIGEST_LENGTH); - FQ_TRACE("SHA1", 9) << "data len:" << len; - FQ_TRACE("SHA1", 9) << "data:\n" << dumpHexString << std::string((char *)data, len); - FQ_TRACE("SHA1", 9) << "digest len:" << tmp; - FQ_TRACE("SHA1", 9) << "digest:\n" << dumpHexString << std::string((char *)digest, tmp); -} - -} // namespace FQTerm diff --git a/src/protocol/internal/fqterm_ssh_mac.h b/src/protocol/internal/fqterm_ssh_mac.h deleted file mode 100644 index 545909c..0000000 --- a/src/protocol/internal/fqterm_ssh_mac.h +++ /dev/null @@ -1,54 +0,0 @@ -/*************************************************************************** - * fqterm, a terminal emulator for both BBS and *nix. * - * Copyright (C) 2008 fqterm development group. * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program 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 General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * - ***************************************************************************/ - -#ifndef FQTERM_SSH_MAC_H -#define FQTERM_SSH_MAC_H - -#include - -namespace FQTerm { - -class FQTermSSHMac { -public: - FQTermSSHMac() {} - virtual ~FQTermSSHMac() {} - virtual void setKey(const unsigned char *key) = 0; - virtual int keySize() const = 0; - virtual int digestSize() const = 0; - virtual void getDigest(const unsigned char *data, int len, unsigned char *digest) const = 0; -}; - -const int FQTERM_SSH_MAC_NONE = 0; -const int FQTERM_SSH_HMAC_SHA1 = 1; - -class FQTermSSHSHA1: public FQTermSSHMac { - unsigned char key_[SHA_DIGEST_LENGTH]; - -public: - virtual void setKey(const unsigned char *key); - virtual int keySize() const; - virtual int digestSize() const; - virtual void getDigest(const unsigned char *data, int len, unsigned char *digest) const; -}; - - -} // namespace FQTerm - -#endif // FQTERM_SSH_MAC_H diff --git a/src/protocol/internal/fqterm_ssh_packet.cpp b/src/protocol/internal/fqterm_ssh_packet.cpp index 402d1b8..d9a8f89 100644 --- a/src/protocol/internal/fqterm_ssh_packet.cpp +++ b/src/protocol/internal/fqterm_ssh_packet.cpp @@ -41,8 +41,7 @@ FQTermSSHPacketSender::FQTermSSHPacketSender() { cipher = NULL; is_mac_ = false; - mac_type_ = FQTERM_SSH_MAC_NONE; - mac_ = NULL; + mac = NULL; is_compressed_ = false; @@ -54,6 +53,8 @@ FQTermSSHPacketSender::~FQTermSSHPacketSender() { delete output_buffer_; if (cipher) cipher->cleanup(cipher); + if (mac) + mac->cleanup(mac); } void FQTermSSHPacketSender::putRawData(const char *data, int len) { @@ -104,22 +105,9 @@ void FQTermSSHPacketSender::resetEncryption() { is_encrypt_ = false; } -void FQTermSSHPacketSender::setMacType(int macType) { - mac_type_ = macType; - - delete mac_; - mac_ = NULL; - - switch (mac_type_) { - case FQTERM_SSH_HMAC_SHA1: - mac_ = new FQTermSSHSHA1; - break; - } -} - void FQTermSSHPacketSender::startMac(const u_char *key) { is_mac_ = true; - mac_->setKey(key); + memcpy(mac->key, key, mac->keySize); } void FQTermSSHPacketSender::resetMac() { @@ -138,8 +126,7 @@ FQTermSSHPacketReceiver::FQTermSSHPacketReceiver() { cipher = NULL; is_mac_ = false; - mac_type_ = FQTERM_SSH_MAC_NONE; - mac_ = NULL; + mac = NULL; is_compressed_ = false; @@ -151,6 +138,8 @@ FQTermSSHPacketReceiver::~FQTermSSHPacketReceiver() delete buffer_; if (cipher) cipher->cleanup(cipher); + if (mac) + mac->cleanup(mac); } void FQTermSSHPacketReceiver::getRawData(char *data, int length) { @@ -195,21 +184,9 @@ void FQTermSSHPacketReceiver::resetEncryption() { is_decrypt_ = false; } -void FQTermSSHPacketReceiver::setMacType(int macType) { - mac_type_ = macType; - delete mac_; - mac_ = NULL; - - switch (mac_type_) { - case FQTERM_SSH_HMAC_SHA1: - mac_ = new FQTermSSHSHA1; - break; - } -} - void FQTermSSHPacketReceiver::startMac(const u_char *key) { is_mac_ = true; - mac_->setKey(key); + memcpy(mac->key, key, mac->keySize); } void FQTermSSHPacketReceiver::resetMac() { diff --git a/src/protocol/internal/fqterm_ssh_packet.h b/src/protocol/internal/fqterm_ssh_packet.h index 705a11b..7caa663 100644 --- a/src/protocol/internal/fqterm_ssh_packet.h +++ b/src/protocol/internal/fqterm_ssh_packet.h @@ -27,7 +27,7 @@ #include "fqterm_ssh_types.h" #include "fqterm_ssh_buffer.h" -#include "fqterm_ssh_mac.h" +#include "ssh_mac.h" #include "fqterm_serialization.h" #include "ssh_cipher.h" @@ -39,6 +39,7 @@ class FQTermSSHPacketSender: public QObject { FQTermSSHBuffer *output_buffer_; FQTermSSHBuffer *buffer_; ssh_cipher_t *cipher; + ssh_mac_t *mac; FQTermSSHPacketSender(); virtual ~FQTermSSHPacketSender(); @@ -54,13 +55,12 @@ class FQTermSSHPacketSender: public QObject { virtual int getIVSize() const { return cipher->IVSize;} virtual int getKeySize() const { return cipher->keySize;} - int getMacKeySize() const { return mac_->keySize();} + int getMacKeySize() const { return mac->keySize;} public slots: void startEncryption(const u_char *key, const u_char *IV = NULL); void resetEncryption(); - void setMacType(int macType); void startMac(const u_char *sessionkey); void resetMac(); @@ -74,8 +74,6 @@ class FQTermSSHPacketSender: public QObject { int cipher_type_; bool is_mac_; - int mac_type_; - FQTermSSHMac *mac_; bool is_compressed_; @@ -89,6 +87,7 @@ class FQTermSSHPacketReceiver: public QObject { public: FQTermSSHBuffer *buffer_; ssh_cipher_t *cipher; + ssh_mac_t *mac; FQTermSSHPacketReceiver(); virtual ~FQTermSSHPacketReceiver(); @@ -108,14 +107,13 @@ class FQTermSSHPacketReceiver: public QObject { virtual int packetDataLen() const { return real_data_len_;} virtual int getIVSize() const { return cipher->IVSize;} virtual int getKeySize() const { return cipher->keySize;} - int getMacKeySize() const { return mac_->keySize();} + int getMacKeySize() const { return mac->keySize;} virtual void parseData(FQTermSSHBuffer *input) = 0; public slots: void startEncryption(const u_char *key, const u_char *IV = NULL); void resetEncryption(); - void setMacType(int macType); void startMac(const u_char *sessionkey); void resetMac(); @@ -130,8 +128,6 @@ class FQTermSSHPacketReceiver: public QObject { int cipher_type_; bool is_mac_; - int mac_type_; - FQTermSSHMac *mac_; bool is_compressed_; diff --git a/src/protocol/internal/ssh_mac.c b/src/protocol/internal/ssh_mac.c new file mode 100644 index 0000000..2062cff --- /dev/null +++ b/src/protocol/internal/ssh_mac.c @@ -0,0 +1,82 @@ +/* + * ssh_mac.c: the MAC algorithms used in SSH + * Copyright (C) 2018 Iru Cai + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 + */ + +#include "ssh_mac.h" +#include "ssh_crypto_common.h" +#include +#include +#include + +static struct ssh_mac_t *new_evp_mac(const struct ssh_mac_t *t) +{ + struct ssh_mac_t *m = (struct ssh_mac_t *)malloc(sizeof(struct ssh_mac_t)); + const EVP_MD *evp_md = ((const EVP_MD *(*)(void))(t->priv))(); + *m = *t; + m->priv = evp_md; + return m; +} + +static void free_evp_mac(struct ssh_mac_t *m) +{ + free(m); +} + +static void get_evp_digest(SSH_MAC *my, const unsigned char *d, int len, unsigned char *dgst) +{ + unsigned int tmp; + HMAC((const EVP_MD *)(my->priv), my->key, my->keySize, d, len, dgst, &tmp); +} + +/* RFC 6668 */ +struct ssh_mac_t hmac_sha2_256 = { + .name = "hmac-sha2-256", + .priv = (void*)EVP_sha256, + .new_mac = new_evp_mac, + .cleanup = free_evp_mac, + .getmac = get_evp_digest, + .dgstSize = 32, + .keySize = 32 +}; + +struct ssh_mac_t hmac_sha1 = { + .name = "hmac-sha1", + .priv = (void*)EVP_sha1, + .new_mac = new_evp_mac, + .cleanup = free_evp_mac, + .getmac = get_evp_digest, + .dgstSize = 20, + .keySize = 20 +}; + +static name_list all_macs = { + { "hmac-sha2-256", &hmac_sha2_256 }, + { "hmac-sha1", &hmac_sha1 }, + { NULL, NULL } +}; + +const struct ssh_mac_t * search_mac(const char *s) +{ + int i = search_name(all_macs, s); + if (i!=-1) + return all_macs[i].f; + else + return NULL; +} + +const char all_macs_list[] = "hmac-sha2-256,hmac-sha1"; diff --git a/src/protocol/internal/ssh_mac.h b/src/protocol/internal/ssh_mac.h new file mode 100644 index 0000000..507a9cd --- /dev/null +++ b/src/protocol/internal/ssh_mac.h @@ -0,0 +1,55 @@ +/* + * ssh_mac.h: the MAC algorithms used in SSH + * Copyright (C) 2018 Iru Cai + * + * 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; either + * version 2.1 of the License, or (at your option) any later version. + * + * 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 SSH_MAC_H +#define SSH_MAC_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct ssh_mac_t SSH_MAC; + typedef SSH_MAC* (*create_t)(const SSH_MAC*); + typedef void (*mac_cleanup_t)(SSH_MAC*); + typedef void (*mac_t)(SSH_MAC*, const unsigned char*, int, unsigned char*); + + struct ssh_mac_t + { + const char *name; + void *priv; + create_t new_mac; + mac_cleanup_t cleanup; + mac_t getmac; + unsigned char key[32]; + size_t dgstSize; + size_t keySize; + }; + + const struct ssh_mac_t *search_mac(const char *s); + extern const char all_macs_list[]; + +#ifdef __cplusplus +} +#endif + +#endif -- cgit v1.2.3