diff options
author | Christian Kandeler <[email protected]> | 2012-05-18 10:49:35 +0200 |
---|---|---|
committer | Christian Kandeler <[email protected]> | 2012-05-22 10:51:53 +0200 |
commit | 53a1087d1321758ab1dc0a6ad737c135249bd5e6 (patch) | |
tree | ab0f9596da64a1907690b324bd05416c585eaf8c /src/libs/ssh/sshkeyexchange.cpp | |
parent | b9d9bb7ba85e3b3ea0e06f0876272c0e3bebd144 (diff) |
Move SSH support into a dedicated library.
It does not belong into libUtils, which is a collection of small
unrelated utility classes.
Task-number: QTCREATORBUG-7218
Change-Id: Id92b9f28678afec93e6f07166adfde6550f38072
Reviewed-by: Eike Ziller <[email protected]>
Diffstat (limited to 'src/libs/ssh/sshkeyexchange.cpp')
-rw-r--r-- | src/libs/ssh/sshkeyexchange.cpp | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/src/libs/ssh/sshkeyexchange.cpp b/src/libs/ssh/sshkeyexchange.cpp new file mode 100644 index 00000000000..333f394deaa --- /dev/null +++ b/src/libs/ssh/sshkeyexchange.cpp @@ -0,0 +1,225 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation ([email protected]) +** +** +** GNU Lesser General Public License Usage +** +** 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. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at [email protected]. +** +**************************************************************************/ + +#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> + +#ifdef CREATOR_SSH_DEBUG +#include <iostream> +#endif +#include <string> + +using namespace Botan; + +namespace QSsh { +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 + } + +#ifdef CREATOR_SSH_DEBUG + void printData(const char *name, const QByteArray &data) + { + std::cerr << std::hex; + qDebug("The client thinks the %s has length %d and is:", name, data.count()); + for (int i = 0; i < data.count(); ++i) + std::cerr << (static_cast<unsigned int>(data.at(i)) & 0xff) << ' '; + std::cerr << std::endl; + } +#endif + +} // anonymous namespace + +SshKeyExchange::SshKeyExchange(SshSendFacility &sendFacility) + : m_sendFacility(sendFacility) +{ +} + +SshKeyExchange::~SshKeyExchange() {} + +void SshKeyExchange::sendKexInitPacket(const QByteArray &serverId) +{ + m_serverId = serverId; + m_clientKexInitPayload = m_sendFacility.sendKeyExchangeInitPacket(); +} + +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)))); + + m_serverKexInitPayload = serverKexInit.payLoad(); + 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); + +#ifdef CREATOR_SSH_DEBUG + printData("Client Id", AbstractSshPacket::encodeString(clientId)); + printData("Server Id", AbstractSshPacket::encodeString(m_serverId)); + printData("Client Payload", AbstractSshPacket::encodeString(m_clientKexInitPayload)); + printData("Server payload", AbstractSshPacket::encodeString(m_serverKexInitPayload)); + printData("K_S", reply.k_s); + printData("y", AbstractSshPacket::encodeMpInt(m_dhKey->get_y())); + printData("f", AbstractSshPacket::encodeMpInt(reply.f)); + printData("K", m_k); + printData("Concatenated data", concatenatedData); + printData("H", m_h); +#endif // CREATOR_SSH_DEBUG + + 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 QSsh |