OSDN Git Service

It's 2011 now.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / coreplugin / ssh / sshkeyexchange.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (qt-info@nokia.com)
8 **
9 ** No Commercial Usage
10 **
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
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 **
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.
24 **
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.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **************************************************************************/
33
34 #include "sshkeyexchange_p.h"
35
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"
41
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>
47
48 #include <string>
49
50 using namespace Botan;
51
52 namespace Core {
53 namespace Internal {
54
55 namespace {
56
57     // For debugging
58     void printNameList(const char *listName, const SshNameList &list)
59     {
60 #ifdef CREATOR_SSH_DEBUG
61         qDebug("%s:", listName);
62         foreach (const QByteArray &name, list.names)
63             qDebug("%s", name.constData());
64 #else
65         Q_UNUSED(listName);
66         Q_UNUSED(list);
67 #endif
68     }
69 } // anonymous namespace
70
71 SshKeyExchange::SshKeyExchange(SshSendFacility &sendFacility)
72     : m_sendFacility(sendFacility)
73 {
74 }
75
76 SshKeyExchange::~SshKeyExchange() {}
77
78 void SshKeyExchange::sendKexInitPacket(const QByteArray &serverId)
79 {
80     m_serverId = serverId;
81     const AbstractSshPacket::Payload &payload
82         = m_sendFacility.sendKeyExchangeInitPacket();
83     m_clientKexInitPayload = QByteArray(payload.data, payload.size);
84 }
85
86 bool SshKeyExchange::sendDhInitPacket(const SshIncomingPacket &serverKexInit)
87 {
88 #ifdef CREATOR_SSH_DEBUG
89     qDebug("server requests key exchange");
90 #endif
91     serverKexInit.printRawBytes();
92     SshKeyExchangeInit kexInitParams
93             = serverKexInit.extractKeyExchangeInitData();
94
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);
107 #endif
108
109     const QByteArray &keyAlgo
110         = SshCapabilities::findBestMatch(SshCapabilities::KeyExchangeMethods,
111               kexInitParams.keyAlgorithms.names);
112     m_serverHostKeyAlgo
113         = SshCapabilities::findBestMatch(SshCapabilities::PublicKeyAlgorithms,
114               kexInitParams.serverHostKeyAlgorithms.names);
115     m_encryptionAlgo
116         = SshCapabilities::findBestMatch(SshCapabilities::EncryptionAlgorithms,
117               kexInitParams.encryptionAlgorithmsClientToServer.names);
118     m_decryptionAlgo
119         = SshCapabilities::findBestMatch(SshCapabilities::EncryptionAlgorithms,
120               kexInitParams.encryptionAlgorithmsServerToClient.names);
121     m_c2sHMacAlgo
122         = SshCapabilities::findBestMatch(SshCapabilities::MacAlgorithms,
123               kexInitParams.macAlgorithmsClientToServer.names);
124     m_s2cHMacAlgo
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);
131
132     AutoSeeded_RNG rng;
133     m_dhKey.reset(new DH_PrivateKey(rng,
134         DL_Group(botanKeyExchangeAlgoName(keyAlgo))));
135
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;
140 }
141
142 void SshKeyExchange::sendNewKeysPacket(const SshIncomingPacket &dhReply,
143     const QByteArray &clientId)
144 {
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.");
150     }
151
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;
162
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);
168
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)));
185     } else {
186         Q_ASSERT(!"Impossible: Neither DSS nor RSA!");
187     }
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.");
195     }
196
197     m_sendFacility.sendNewKeysPacket();
198 }
199
200 } // namespace Internal
201 } // namespace Core