Mercurial > 510Connectbot
comparison src/ch/ethz/ssh2/transport/KexManager.java @ 273:91a31873c42a ganymed
start conversion from trilead to ganymed
author | Carl Byington <carl@five-ten-sg.com> |
---|---|
date | Fri, 18 Jul 2014 11:21:46 -0700 |
parents | |
children | d7e088fa2123 |
comparison
equal
deleted
inserted
replaced
272:ce2f4e397703 | 273:91a31873c42a |
---|---|
1 /* | |
2 * Copyright (c) 2006-2011 Christian Plattner. All rights reserved. | |
3 * Please refer to the LICENSE.txt for licensing details. | |
4 */ | |
5 package ch.ethz.ssh2.transport; | |
6 | |
7 import java.io.IOException; | |
8 import java.io.InterruptedIOException; | |
9 import java.security.DigestException; | |
10 import java.security.SecureRandom; | |
11 import java.util.Arrays; | |
12 | |
13 import ch.ethz.ssh2.ConnectionInfo; | |
14 import ch.ethz.ssh2.DHGexParameters; | |
15 import ch.ethz.ssh2.compression.CompressionFactory; | |
16 import ch.ethz.ssh2.compression.Compressor; | |
17 import ch.ethz.ssh2.crypto.CryptoWishList; | |
18 import ch.ethz.ssh2.crypto.KeyMaterial; | |
19 import ch.ethz.ssh2.crypto.cipher.BlockCipher; | |
20 import ch.ethz.ssh2.crypto.cipher.BlockCipherFactory; | |
21 import ch.ethz.ssh2.crypto.digest.MAC; | |
22 import ch.ethz.ssh2.log.Logger; | |
23 import ch.ethz.ssh2.packets.PacketKexInit; | |
24 import ch.ethz.ssh2.packets.PacketNewKeys; | |
25 import ch.ethz.ssh2.signature.DSAPrivateKey; | |
26 import ch.ethz.ssh2.signature.RSAPrivateKey; | |
27 | |
28 /** | |
29 * @version $Id: KexManager.java 152 2014-04-28 11:02:23Z dkocher@sudo.ch $ | |
30 */ | |
31 public abstract class KexManager implements MessageHandler { | |
32 protected static final Logger log = Logger.getLogger(KexManager.class); | |
33 | |
34 KexState kxs; | |
35 int kexCount = 0; | |
36 KeyMaterial km; | |
37 byte[] sessionId; | |
38 ClientServerHello csh; | |
39 | |
40 final Object accessLock = new Object(); | |
41 ConnectionInfo lastConnInfo = null; | |
42 | |
43 boolean connectionClosed = false; | |
44 | |
45 boolean ignore_next_kex_packet = false; | |
46 | |
47 final TransportManager tm; | |
48 | |
49 CryptoWishList nextKEXcryptoWishList; | |
50 DHGexParameters nextKEXdhgexParameters; | |
51 DSAPrivateKey nextKEXdsakey; | |
52 RSAPrivateKey nextKEXrsakey; | |
53 | |
54 final SecureRandom rnd; | |
55 | |
56 public KexManager(TransportManager tm, ClientServerHello csh, CryptoWishList initialCwl, SecureRandom rnd) { | |
57 this.tm = tm; | |
58 this.csh = csh; | |
59 this.nextKEXcryptoWishList = initialCwl; | |
60 this.nextKEXdhgexParameters = new DHGexParameters(); | |
61 this.rnd = rnd; | |
62 } | |
63 | |
64 public ConnectionInfo getOrWaitForConnectionInfo(int minKexCount) throws IOException { | |
65 synchronized(accessLock) { | |
66 while(true) { | |
67 if((lastConnInfo != null) && (lastConnInfo.keyExchangeCounter >= minKexCount)) { | |
68 return lastConnInfo; | |
69 } | |
70 if(connectionClosed) { | |
71 throw tm.getReasonClosedCause(); | |
72 } | |
73 try { | |
74 accessLock.wait(); | |
75 } | |
76 catch(InterruptedException e) { | |
77 throw new InterruptedIOException(e.getMessage()); | |
78 } | |
79 } | |
80 } | |
81 } | |
82 | |
83 private String getFirstMatch(String[] client, String[] server) throws NegotiateException { | |
84 if(client == null || server == null) { | |
85 throw new IllegalArgumentException(); | |
86 } | |
87 for(String c : client) { | |
88 for(String s : server) { | |
89 if(c.equals(s)) { | |
90 return c; | |
91 } | |
92 } | |
93 } | |
94 throw new NegotiateException(String.format("Negotiation failed for %s", Arrays.toString(server))); | |
95 } | |
96 | |
97 private boolean compareFirstOfNameList(String[] a, String[] b) { | |
98 if(a == null || b == null) { | |
99 throw new IllegalArgumentException(); | |
100 } | |
101 if((a.length == 0) && (b.length == 0)) { | |
102 return true; | |
103 } | |
104 if((a.length == 0) || (b.length == 0)) { | |
105 return false; | |
106 } | |
107 return (a[0].equals(b[0])); | |
108 } | |
109 | |
110 private boolean isGuessOK(KexParameters cpar, KexParameters spar) { | |
111 if(cpar == null || spar == null) { | |
112 throw new IllegalArgumentException(); | |
113 } | |
114 if(!compareFirstOfNameList(cpar.kex_algorithms, spar.kex_algorithms)) { | |
115 return false; | |
116 } | |
117 if(!compareFirstOfNameList(cpar.server_host_key_algorithms, spar.server_host_key_algorithms)) { | |
118 return false; | |
119 } | |
120 | |
121 /* | |
122 * We do NOT check here if the other algorithms can be agreed on, this | |
123 * is just a check if kex_algorithms and server_host_key_algorithms were | |
124 * guessed right! | |
125 */ | |
126 | |
127 return true; | |
128 } | |
129 | |
130 protected NegotiatedParameters mergeKexParameters(KexParameters client, KexParameters server) | |
131 throws NegotiateException { | |
132 NegotiatedParameters np = new NegotiatedParameters(); | |
133 | |
134 np.kex_algo = getFirstMatch(client.kex_algorithms, server.kex_algorithms); | |
135 | |
136 log.info("kex_algo=" + np.kex_algo); | |
137 | |
138 np.server_host_key_algo = getFirstMatch(client.server_host_key_algorithms, | |
139 server.server_host_key_algorithms); | |
140 | |
141 log.info("server_host_key_algo=" + np.server_host_key_algo); | |
142 | |
143 np.enc_algo_client_to_server = getFirstMatch(client.encryption_algorithms_client_to_server, | |
144 server.encryption_algorithms_client_to_server); | |
145 np.enc_algo_server_to_client = getFirstMatch(client.encryption_algorithms_server_to_client, | |
146 server.encryption_algorithms_server_to_client); | |
147 | |
148 log.info("enc_algo_client_to_server=" + np.enc_algo_client_to_server); | |
149 log.info("enc_algo_server_to_client=" + np.enc_algo_server_to_client); | |
150 | |
151 np.mac_algo_client_to_server = getFirstMatch(client.mac_algorithms_client_to_server, | |
152 server.mac_algorithms_client_to_server); | |
153 np.mac_algo_server_to_client = getFirstMatch(client.mac_algorithms_server_to_client, | |
154 server.mac_algorithms_server_to_client); | |
155 | |
156 log.info("mac_algo_client_to_server=" + np.mac_algo_client_to_server); | |
157 log.info("mac_algo_server_to_client=" + np.mac_algo_server_to_client); | |
158 | |
159 np.comp_algo_client_to_server = getFirstMatch(client.compression_algorithms_client_to_server, | |
160 server.compression_algorithms_client_to_server); | |
161 np.comp_algo_server_to_client = getFirstMatch(client.compression_algorithms_server_to_client, | |
162 server.compression_algorithms_server_to_client); | |
163 | |
164 log.info("comp_algo_client_to_server=" + np.comp_algo_client_to_server); | |
165 log.info("comp_algo_server_to_client=" + np.comp_algo_server_to_client); | |
166 | |
167 np.lang_client_to_server = getFirstMatch(client.languages_client_to_server, | |
168 server.languages_client_to_server); | |
169 | |
170 np.lang_server_to_client = getFirstMatch(client.languages_server_to_client, | |
171 server.languages_server_to_client); | |
172 | |
173 if(isGuessOK(client, server)) { | |
174 np.guessOK = true; | |
175 } | |
176 return np; | |
177 } | |
178 | |
179 public synchronized void initiateKEX(CryptoWishList cwl, DHGexParameters dhgex, DSAPrivateKey dsa, RSAPrivateKey rsa) | |
180 throws IOException { | |
181 nextKEXcryptoWishList = cwl; | |
182 nextKEXdhgexParameters = dhgex; | |
183 nextKEXdsakey = dsa; | |
184 nextKEXrsakey = rsa; | |
185 | |
186 if(kxs == null) { | |
187 kxs = new KexState(); | |
188 kxs.local_dsa_key = dsa; | |
189 kxs.local_rsa_key = rsa; | |
190 kxs.dhgexParameters = nextKEXdhgexParameters; | |
191 kxs.localKEX = new PacketKexInit(nextKEXcryptoWishList, rnd); | |
192 tm.sendKexMessage(kxs.localKEX.getPayload()); | |
193 } | |
194 } | |
195 | |
196 private boolean establishKeyMaterial() throws IOException { | |
197 try { | |
198 int mac_cs_key_len = MAC.getKeyLen(kxs.np.mac_algo_client_to_server); | |
199 int enc_cs_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_client_to_server); | |
200 int enc_cs_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_client_to_server); | |
201 | |
202 int mac_sc_key_len = MAC.getKeyLen(kxs.np.mac_algo_server_to_client); | |
203 int enc_sc_key_len = BlockCipherFactory.getKeySize(kxs.np.enc_algo_server_to_client); | |
204 int enc_sc_block_len = BlockCipherFactory.getBlockSize(kxs.np.enc_algo_server_to_client); | |
205 | |
206 km = KeyMaterial.create("SHA1", kxs.H, kxs.K, sessionId, enc_cs_key_len, enc_cs_block_len, mac_cs_key_len, | |
207 enc_sc_key_len, enc_sc_block_len, mac_sc_key_len); | |
208 } | |
209 catch(IllegalArgumentException e) { | |
210 return false; | |
211 } | |
212 return true; | |
213 } | |
214 | |
215 protected void finishKex(boolean clientMode) throws IOException { | |
216 if(sessionId == null) { | |
217 sessionId = kxs.H; | |
218 } | |
219 | |
220 establishKeyMaterial(); | |
221 | |
222 /* Tell the other side that we start using the new material */ | |
223 | |
224 PacketNewKeys ign = new PacketNewKeys(); | |
225 tm.sendKexMessage(ign.getPayload()); | |
226 | |
227 BlockCipher cbc; | |
228 MAC mac; | |
229 Compressor comp; | |
230 | |
231 try { | |
232 cbc = BlockCipherFactory.createCipher(clientMode ? kxs.np.enc_algo_client_to_server | |
233 : kxs.np.enc_algo_server_to_client, true, clientMode ? km.enc_key_client_to_server | |
234 : km.enc_key_server_to_client, clientMode ? km.initial_iv_client_to_server | |
235 : km.initial_iv_server_to_client); | |
236 | |
237 try { | |
238 mac = new MAC(clientMode ? kxs.np.mac_algo_client_to_server : kxs.np.mac_algo_server_to_client, clientMode | |
239 ? km.integrity_key_client_to_server : km.integrity_key_server_to_client); | |
240 } | |
241 catch(DigestException e) { | |
242 throw new IOException(e); | |
243 } | |
244 | |
245 comp = CompressionFactory.createCompressor(kxs.np.comp_algo_client_to_server); | |
246 } | |
247 catch(IllegalArgumentException f) { | |
248 throw new IOException(String.format("Fatal error initializing ciphers. %s", f.getMessage())); | |
249 } | |
250 | |
251 tm.changeSendCipher(cbc, mac); | |
252 tm.changeSendCompression(comp); | |
253 tm.kexFinished(); | |
254 } | |
255 | |
256 public static String[] getDefaultServerHostkeyAlgorithmList() { | |
257 return new String[]{"ssh-rsa", "ssh-dss"}; | |
258 } | |
259 | |
260 public static void checkServerHostkeyAlgorithmsList(String[] algos) { | |
261 for(final String algo : algos) { | |
262 if("ssh-rsa".equals(algo)) { | |
263 continue; | |
264 } | |
265 if("ssh-dss".equals(algo)) { | |
266 continue; | |
267 } | |
268 throw new IllegalArgumentException(String.format("Unknown server host key algorithm %s", algo)); | |
269 } | |
270 } | |
271 | |
272 public static String[] getDefaultClientKexAlgorithmList() { | |
273 return new String[]{"diffie-hellman-group-exchange-sha1", "diffie-hellman-group14-sha1", | |
274 "diffie-hellman-group1-sha1"}; | |
275 } | |
276 | |
277 public static String[] getDefaultServerKexAlgorithmList() { | |
278 return new String[]{"diffie-hellman-group14-sha1", "diffie-hellman-group1-sha1"}; | |
279 } | |
280 | |
281 public static void checkKexAlgorithmList(String[] algos) { | |
282 for(final String algo : algos) { | |
283 if("diffie-hellman-group-exchange-sha1".equals(algo)) { | |
284 continue; | |
285 } | |
286 if("diffie-hellman-group14-sha1".equals(algo)) { | |
287 continue; | |
288 } | |
289 if("diffie-hellman-group1-sha1".equals(algo)) { | |
290 continue; | |
291 } | |
292 throw new IllegalArgumentException(String.format("Unknown kex algorithm %s", algo)); | |
293 } | |
294 } | |
295 } |