diff options
Diffstat (limited to 'coreplugin/ssh/sshkeyexchange.cpp')
-rw-r--r-- | coreplugin/ssh/sshkeyexchange.cpp | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/coreplugin/ssh/sshkeyexchange.cpp b/coreplugin/ssh/sshkeyexchange.cpp new file mode 100644 index 0000000..7875d2e --- /dev/null +++ b/coreplugin/ssh/sshkeyexchange.cpp @@ -0,0 +1,197 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "sshkeyexchange_p.h" + +#include "sshbotanconversions_p.h" +#include "sshcapabilities_p.h" +#include "sshsendfacility_p.h" +#include "sshexception_p.h" +#include "sshincomingpacket_p.h" + +#include <botan/botan.h> +#include <botan/dsa.h> +#include <botan/look_pk.h> +#include <botan/pubkey.h> +#include <botan/rsa.h> + +#include <string> + +using namespace Botan; + +namespace Core { +namespace Internal { + +namespace { + + // For debugging + void printNameList(const char *listName, const SshNameList &list) + { +#ifdef CREATOR_SSH_DEBUG + qDebug("%s:", listName); + foreach (const QByteArray &name, list.names) + qDebug("%s", name.constData()); +#else + Q_UNUSED(listName); + Q_UNUSED(list); +#endif + } +} // anonymous namespace + +SshKeyExchange::SshKeyExchange(SshSendFacility &sendFacility) + : m_sendFacility(sendFacility) +{ +} + +SshKeyExchange::~SshKeyExchange() {} + +void SshKeyExchange::sendKexInitPacket(const QByteArray &serverId) +{ + m_serverId = serverId; + const AbstractSshPacket::Payload &payload + = m_sendFacility.sendKeyExchangeInitPacket(); + m_clientKexInitPayload = QByteArray(payload.data, payload.size); +} + +bool SshKeyExchange::sendDhInitPacket(const SshIncomingPacket &serverKexInit) +{ +#ifdef CREATOR_SSH_DEBUG + qDebug("server requests key exchange"); +#endif + serverKexInit.printRawBytes(); + SshKeyExchangeInit kexInitParams + = serverKexInit.extractKeyExchangeInitData(); + + printNameList("Key Algorithms", kexInitParams.keyAlgorithms); + printNameList("Server Host Key Algorithms", kexInitParams.serverHostKeyAlgorithms); + printNameList("Encryption algorithms client to server", kexInitParams.encryptionAlgorithmsClientToServer); + printNameList("Encryption algorithms server to client", kexInitParams.encryptionAlgorithmsServerToClient); + printNameList("MAC algorithms client to server", kexInitParams.macAlgorithmsClientToServer); + printNameList("MAC algorithms server to client", kexInitParams.macAlgorithmsServerToClient); + printNameList("Compression algorithms client to server", kexInitParams.compressionAlgorithmsClientToServer); + printNameList("Compression algorithms client to server", kexInitParams.compressionAlgorithmsClientToServer); + printNameList("Languages client to server", kexInitParams.languagesClientToServer); + printNameList("Languages server to client", kexInitParams.languagesServerToClient); +#ifdef CREATOR_SSH_DEBUG + qDebug("First packet follows: %d", kexInitParams.firstKexPacketFollows); +#endif + + const QByteArray &keyAlgo + = SshCapabilities::findBestMatch(SshCapabilities::KeyExchangeMethods, + kexInitParams.keyAlgorithms.names); + m_serverHostKeyAlgo + = SshCapabilities::findBestMatch(SshCapabilities::PublicKeyAlgorithms, + kexInitParams.serverHostKeyAlgorithms.names); + m_encryptionAlgo + = SshCapabilities::findBestMatch(SshCapabilities::EncryptionAlgorithms, + kexInitParams.encryptionAlgorithmsClientToServer.names); + m_decryptionAlgo + = SshCapabilities::findBestMatch(SshCapabilities::EncryptionAlgorithms, + kexInitParams.encryptionAlgorithmsServerToClient.names); + m_c2sHMacAlgo + = SshCapabilities::findBestMatch(SshCapabilities::MacAlgorithms, + kexInitParams.macAlgorithmsClientToServer.names); + m_s2cHMacAlgo + = SshCapabilities::findBestMatch(SshCapabilities::MacAlgorithms, + kexInitParams.macAlgorithmsServerToClient.names); + SshCapabilities::findBestMatch(SshCapabilities::CompressionAlgorithms, + kexInitParams.compressionAlgorithmsClientToServer.names); + SshCapabilities::findBestMatch(SshCapabilities::CompressionAlgorithms, + kexInitParams.compressionAlgorithmsServerToClient.names); + + AutoSeeded_RNG rng; + m_dhKey.reset(new DH_PrivateKey(rng, + DL_Group(botanKeyExchangeAlgoName(keyAlgo)))); + + const AbstractSshPacket::Payload &payload = serverKexInit.payLoad(); + m_serverKexInitPayload = QByteArray(payload.data, payload.size); + m_sendFacility.sendKeyDhInitPacket(m_dhKey->get_y()); + return kexInitParams.firstKexPacketFollows; +} + +void SshKeyExchange::sendNewKeysPacket(const SshIncomingPacket &dhReply, + const QByteArray &clientId) +{ + const SshKeyExchangeReply &reply + = dhReply.extractKeyExchangeReply(m_serverHostKeyAlgo); + if (reply.f <= 0 || reply.f >= m_dhKey->group_p()) { + throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + "Server sent invalid f."); + } + + QByteArray concatenatedData = AbstractSshPacket::encodeString(clientId); + concatenatedData += AbstractSshPacket::encodeString(m_serverId); + concatenatedData += AbstractSshPacket::encodeString(m_clientKexInitPayload); + concatenatedData += AbstractSshPacket::encodeString(m_serverKexInitPayload); + concatenatedData += reply.k_s; + concatenatedData += AbstractSshPacket::encodeMpInt(m_dhKey->get_y()); + concatenatedData += AbstractSshPacket::encodeMpInt(reply.f); + SymmetricKey k = m_dhKey->derive_key(reply.f); + m_k = AbstractSshPacket::encodeMpInt(BigInt(k.begin(), k.length())); + concatenatedData += m_k; + + m_hash.reset(get_hash(botanSha1Name())); + const SecureVector<byte> &hashResult + = m_hash->process(convertByteArray(concatenatedData), + concatenatedData.size()); + m_h = convertByteArray(hashResult); + + QScopedPointer<Public_Key> sigKey; + QScopedPointer<PK_Verifier> verifier; + if (m_serverHostKeyAlgo == SshCapabilities::PubKeyDss) { + const DL_Group group(reply.parameters.at(0), reply.parameters.at(1), + reply.parameters.at(2)); + DSA_PublicKey * const dsaKey + = new DSA_PublicKey(group, reply.parameters.at(3)); + sigKey.reset(dsaKey); + verifier.reset(get_pk_verifier(*dsaKey, + botanEmsaAlgoName(SshCapabilities::PubKeyDss))); + } else if (m_serverHostKeyAlgo == SshCapabilities::PubKeyRsa) { + RSA_PublicKey * const rsaKey + = new RSA_PublicKey(reply.parameters.at(1), reply.parameters.at(0)); + sigKey.reset(rsaKey); + verifier.reset(get_pk_verifier(*rsaKey, + botanEmsaAlgoName(SshCapabilities::PubKeyRsa))); + } else { + Q_ASSERT(!"Impossible: Neither DSS nor RSA!"); + } + const byte * const botanH = convertByteArray(m_h); + const Botan::byte * const botanSig + = convertByteArray(reply.signatureBlob); + if (!verifier->verify_message(botanH, m_h.size(), botanSig, + reply.signatureBlob.size())) { + throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_KEY_EXCHANGE_FAILED, + "Invalid signature in SSH_MSG_KEXDH_REPLY packet."); + } + + m_sendFacility.sendNewKeysPacket(); +} + +} // namespace Internal +} // namespace Core |