aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs/ssh/sshkeyexchange.cpp
diff options
context:
space:
mode:
authorChristian Kandeler <[email protected]>2012-05-18 10:49:35 +0200
committerChristian Kandeler <[email protected]>2012-05-22 10:51:53 +0200
commit53a1087d1321758ab1dc0a6ad737c135249bd5e6 (patch)
treeab0f9596da64a1907690b324bd05416c585eaf8c /src/libs/ssh/sshkeyexchange.cpp
parentb9d9bb7ba85e3b3ea0e06f0876272c0e3bebd144 (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.cpp225
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