1 /**************************************************************************
3 ** This file is part of Qt Creator
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
16 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights. These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
32 **************************************************************************/
34 #include "sshkeyexchange_p.h"
36 #include "sshbotanconversions_p.h"
37 #include "sshcapabilities_p.h"
38 #include "sshsendfacility_p.h"
39 #include "sshexception_p.h"
40 #include "sshincomingpacket_p.h"
42 #include <botan/botan.h>
43 #include <botan/dsa.h>
44 #include <botan/look_pk.h>
45 #include <botan/pubkey.h>
46 #include <botan/rsa.h>
50 using namespace Botan;
58 void printNameList(const char *listName, const SshNameList &list)
60 #ifdef CREATOR_SSH_DEBUG
61 qDebug("%s:", listName);
62 foreach (const QByteArray &name, list.names)
63 qDebug("%s", name.constData());
69 } // anonymous namespace
71 SshKeyExchange::SshKeyExchange(SshSendFacility &sendFacility)
72 : m_sendFacility(sendFacility)
76 SshKeyExchange::~SshKeyExchange() {}
78 void SshKeyExchange::sendKexInitPacket(const QByteArray &serverId)
80 m_serverId = serverId;
81 const AbstractSshPacket::Payload &payload
82 = m_sendFacility.sendKeyExchangeInitPacket();
83 m_clientKexInitPayload = QByteArray(payload.data, payload.size);
86 bool SshKeyExchange::sendDhInitPacket(const SshIncomingPacket &serverKexInit)
88 #ifdef CREATOR_SSH_DEBUG
89 qDebug("server requests key exchange");
91 serverKexInit.printRawBytes();
92 SshKeyExchangeInit kexInitParams
93 = serverKexInit.extractKeyExchangeInitData();
95 printNameList("Key Algorithms", kexInitParams.keyAlgorithms);
96 printNameList("Server Host Key Algorithms", kexInitParams.serverHostKeyAlgorithms);
97 printNameList("Encryption algorithms client to server", kexInitParams.encryptionAlgorithmsClientToServer);
98 printNameList("Encryption algorithms server to client", kexInitParams.encryptionAlgorithmsServerToClient);
99 printNameList("MAC algorithms client to server", kexInitParams.macAlgorithmsClientToServer);
100 printNameList("MAC algorithms server to client", kexInitParams.macAlgorithmsServerToClient);
101 printNameList("Compression algorithms client to server", kexInitParams.compressionAlgorithmsClientToServer);
102 printNameList("Compression algorithms client to server", kexInitParams.compressionAlgorithmsClientToServer);
103 printNameList("Languages client to server", kexInitParams.languagesClientToServer);
104 printNameList("Languages server to client", kexInitParams.languagesServerToClient);
105 #ifdef CREATOR_SSH_DEBUG
106 qDebug("First packet follows: %d", kexInitParams.firstKexPacketFollows);
109 const QByteArray &keyAlgo
110 = SshCapabilities::findBestMatch(SshCapabilities::KeyExchangeMethods,
111 kexInitParams.keyAlgorithms.names);
113 = SshCapabilities::findBestMatch(SshCapabilities::PublicKeyAlgorithms,
114 kexInitParams.serverHostKeyAlgorithms.names);
116 = SshCapabilities::findBestMatch(SshCapabilities::EncryptionAlgorithms,
117 kexInitParams.encryptionAlgorithmsClientToServer.names);
119 = SshCapabilities::findBestMatch(SshCapabilities::EncryptionAlgorithms,
120 kexInitParams.encryptionAlgorithmsServerToClient.names);
122 = SshCapabilities::findBestMatch(SshCapabilities::MacAlgorithms,
123 kexInitParams.macAlgorithmsClientToServer.names);
125 = SshCapabilities::findBestMatch(SshCapabilities::MacAlgorithms,
126 kexInitParams.macAlgorithmsServerToClient.names);
127 SshCapabilities::findBestMatch(SshCapabilities::CompressionAlgorithms,
128 kexInitParams.compressionAlgorithmsClientToServer.names);
129 SshCapabilities::findBestMatch(SshCapabilities::CompressionAlgorithms,
130 kexInitParams.compressionAlgorithmsServerToClient.names);
133 m_dhKey.reset(new DH_PrivateKey(rng,
134 DL_Group(botanKeyExchangeAlgoName(keyAlgo))));
136 const AbstractSshPacket::Payload &payload = serverKexInit.payLoad();
137 m_serverKexInitPayload = QByteArray(payload.data, payload.size);
138 m_sendFacility.sendKeyDhInitPacket(m_dhKey->get_y());
139 return kexInitParams.firstKexPacketFollows;
142 void SshKeyExchange::sendNewKeysPacket(const SshIncomingPacket &dhReply,
143 const QByteArray &clientId)
145 const SshKeyExchangeReply &reply
146 = dhReply.extractKeyExchangeReply(m_serverHostKeyAlgo);
147 if (reply.f <= 0 || reply.f >= m_dhKey->group_p()) {
148 throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
149 "Server sent invalid f.");
152 QByteArray concatenatedData = AbstractSshPacket::encodeString(clientId);
153 concatenatedData += AbstractSshPacket::encodeString(m_serverId);
154 concatenatedData += AbstractSshPacket::encodeString(m_clientKexInitPayload);
155 concatenatedData += AbstractSshPacket::encodeString(m_serverKexInitPayload);
156 concatenatedData += reply.k_s;
157 concatenatedData += AbstractSshPacket::encodeMpInt(m_dhKey->get_y());
158 concatenatedData += AbstractSshPacket::encodeMpInt(reply.f);
159 SymmetricKey k = m_dhKey->derive_key(reply.f);
160 m_k = AbstractSshPacket::encodeMpInt(BigInt(k.begin(), k.length()));
161 concatenatedData += m_k;
163 m_hash.reset(get_hash(botanSha1Name()));
164 const SecureVector<byte> &hashResult
165 = m_hash->process(convertByteArray(concatenatedData),
166 concatenatedData.size());
167 m_h = convertByteArray(hashResult);
169 QScopedPointer<Public_Key> sigKey;
170 QScopedPointer<PK_Verifier> verifier;
171 if (m_serverHostKeyAlgo == SshCapabilities::PubKeyDss) {
172 const DL_Group group(reply.parameters.at(0), reply.parameters.at(1),
173 reply.parameters.at(2));
174 DSA_PublicKey * const dsaKey
175 = new DSA_PublicKey(group, reply.parameters.at(3));
176 sigKey.reset(dsaKey);
177 verifier.reset(get_pk_verifier(*dsaKey,
178 botanEmsaAlgoName(SshCapabilities::PubKeyDss)));
179 } else if (m_serverHostKeyAlgo == SshCapabilities::PubKeyRsa) {
180 RSA_PublicKey * const rsaKey
181 = new RSA_PublicKey(reply.parameters.at(1), reply.parameters.at(0));
182 sigKey.reset(rsaKey);
183 verifier.reset(get_pk_verifier(*rsaKey,
184 botanEmsaAlgoName(SshCapabilities::PubKeyRsa)));
186 Q_ASSERT(!"Impossible: Neither DSS nor RSA!");
188 const byte * const botanH = convertByteArray(m_h);
189 const Botan::byte * const botanSig
190 = convertByteArray(reply.signatureBlob);
191 if (!verifier->verify_message(botanH, m_h.size(), botanSig,
192 reply.signatureBlob.size())) {
193 throw SSH_SERVER_EXCEPTION(SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
194 "Invalid signature in SSH_MSG_KEXDH_REPLY packet.");
197 m_sendFacility.sendNewKeysPacket();
200 } // namespace Internal