Mercurial > 510Connectbot
comparison src/ch/ethz/ssh2/transport/TransportConnection.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 | 071eccdff8ea |
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.InputStream; | |
9 import java.io.OutputStream; | |
10 import java.security.SecureRandom; | |
11 | |
12 import ch.ethz.ssh2.PacketFormatException; | |
13 import ch.ethz.ssh2.compression.Compressor; | |
14 import ch.ethz.ssh2.crypto.cipher.BlockCipher; | |
15 import ch.ethz.ssh2.crypto.cipher.CipherInputStream; | |
16 import ch.ethz.ssh2.crypto.cipher.CipherOutputStream; | |
17 import ch.ethz.ssh2.crypto.cipher.NullCipher; | |
18 import ch.ethz.ssh2.crypto.digest.MAC; | |
19 import ch.ethz.ssh2.log.Logger; | |
20 import ch.ethz.ssh2.packets.Packets; | |
21 | |
22 /** | |
23 * TransportConnection. | |
24 * | |
25 * @author Christian Plattner | |
26 * @version $Id: TransportConnection.java 144 2014-04-25 12:48:25Z dkocher@sudo.ch $ | |
27 */ | |
28 public class TransportConnection { | |
29 private static final Logger log = Logger.getLogger(TransportConnection.class); | |
30 | |
31 int send_seq_number = 0; | |
32 | |
33 int recv_seq_number = 0; | |
34 | |
35 CipherInputStream cis; | |
36 | |
37 CipherOutputStream cos; | |
38 | |
39 boolean useRandomPadding; | |
40 | |
41 /* Depends on current MAC and CIPHER */ | |
42 | |
43 MAC send_mac; | |
44 | |
45 byte[] send_mac_buffer; | |
46 | |
47 int send_padd_blocksize = 8; | |
48 | |
49 MAC recv_mac; | |
50 | |
51 byte[] recv_mac_buffer; | |
52 | |
53 byte[] recv_mac_buffer_cmp; | |
54 | |
55 int recv_padd_blocksize = 8; | |
56 | |
57 Compressor recv_comp; | |
58 | |
59 Compressor send_comp; | |
60 | |
61 boolean can_compress; | |
62 | |
63 byte[] recv_comp_buffer; | |
64 | |
65 byte[] send_comp_buffer; | |
66 | |
67 /* won't change */ | |
68 | |
69 final byte[] send_padding_buffer = new byte[256]; | |
70 | |
71 final byte[] send_packet_header_buffer = new byte[5]; | |
72 | |
73 final byte[] recv_padding_buffer = new byte[256]; | |
74 | |
75 final byte[] recv_packet_header_buffer = new byte[5]; | |
76 | |
77 boolean recv_packet_header_present = false; | |
78 | |
79 ClientServerHello csh; | |
80 | |
81 final SecureRandom rnd; | |
82 | |
83 public TransportConnection(InputStream is, OutputStream os, SecureRandom rnd) { | |
84 this.cis = new CipherInputStream(new NullCipher(), is); | |
85 this.cos = new CipherOutputStream(new NullCipher(), os); | |
86 this.rnd = rnd; | |
87 } | |
88 | |
89 public void changeRecvCipher(BlockCipher bc, MAC mac) { | |
90 cis.changeCipher(bc); | |
91 recv_mac = mac; | |
92 recv_mac_buffer = (mac != null) ? new byte[mac.size()] : null; | |
93 recv_mac_buffer_cmp = (mac != null) ? new byte[mac.size()] : null; | |
94 recv_padd_blocksize = bc.getBlockSize(); | |
95 if(recv_padd_blocksize < 8) { | |
96 recv_padd_blocksize = 8; | |
97 } | |
98 } | |
99 | |
100 public void changeSendCipher(BlockCipher bc, MAC mac) { | |
101 if((bc instanceof NullCipher) == false) { | |
102 /* Only use zero byte padding for the first few packets */ | |
103 useRandomPadding = true; | |
104 /* Once we start encrypting, there is no way back */ | |
105 } | |
106 | |
107 cos.changeCipher(bc); | |
108 send_mac = mac; | |
109 send_mac_buffer = (mac != null) ? new byte[mac.size()] : null; | |
110 send_padd_blocksize = bc.getBlockSize(); | |
111 if(send_padd_blocksize < 8) { | |
112 send_padd_blocksize = 8; | |
113 } | |
114 } | |
115 | |
116 public void changeRecvCompression(Compressor comp) { | |
117 recv_comp = comp; | |
118 | |
119 if(comp != null) { | |
120 recv_comp_buffer = new byte[comp.getBufferSize()]; | |
121 } | |
122 } | |
123 | |
124 public void changeSendCompression(Compressor comp) { | |
125 send_comp = comp; | |
126 | |
127 if(comp != null) { | |
128 send_comp_buffer = new byte[comp.getBufferSize()]; | |
129 } | |
130 } | |
131 | |
132 public void sendMessage(byte[] message) throws IOException { | |
133 sendMessage(message, 0, message.length, 0); | |
134 } | |
135 | |
136 public void sendMessage(byte[] message, int off, int len) throws IOException { | |
137 sendMessage(message, off, len, 0); | |
138 } | |
139 | |
140 public int getPacketOverheadEstimate() { | |
141 // return an estimate for the paket overhead (for send operations) | |
142 return 5 + 4 + (send_padd_blocksize - 1) + send_mac_buffer.length; | |
143 } | |
144 | |
145 public void sendMessage(byte[] message, int off, int len, int padd) throws IOException { | |
146 if(padd < 4) { | |
147 padd = 4; | |
148 } | |
149 else if(padd > 64) { | |
150 padd = 64; | |
151 } | |
152 | |
153 if(send_comp != null && can_compress) { | |
154 len = send_comp.compress(message, off, len, send_comp_buffer); | |
155 message = send_comp_buffer; | |
156 } | |
157 | |
158 int packet_len = 5 + len + padd; /* Minimum allowed padding is 4 */ | |
159 | |
160 int slack = packet_len % send_padd_blocksize; | |
161 | |
162 if(slack != 0) { | |
163 packet_len += (send_padd_blocksize - slack); | |
164 } | |
165 | |
166 if(packet_len < 16) { | |
167 packet_len = 16; | |
168 } | |
169 | |
170 int padd_len = packet_len - (5 + len); | |
171 | |
172 if(useRandomPadding) { | |
173 for(int i = 0; i < padd_len; i = i + 4) { | |
174 /* | |
175 * don't waste calls to rnd.nextInt() (by using only 8bit of the | |
176 * output). just believe me: even though we may write here up to 3 | |
177 * bytes which won't be used, there is no "buffer overflow" (i.e., | |
178 * arrayindexoutofbounds). the padding buffer is big enough =) (256 | |
179 * bytes, and that is bigger than any current cipher block size + 64). | |
180 */ | |
181 | |
182 int r = rnd.nextInt(); | |
183 send_padding_buffer[i] = (byte) r; | |
184 send_padding_buffer[i + 1] = (byte) (r >> 8); | |
185 send_padding_buffer[i + 2] = (byte) (r >> 16); | |
186 send_padding_buffer[i + 3] = (byte) (r >> 24); | |
187 } | |
188 } | |
189 else { | |
190 /* use zero padding for unencrypted traffic */ | |
191 for(int i = 0; i < padd_len; i++) { | |
192 send_padding_buffer[i] = 0; | |
193 } | |
194 /* Actually this code is paranoid: we never filled any | |
195 * bytes into the padding buffer so far, therefore it should | |
196 * consist of zeros only. | |
197 */ | |
198 } | |
199 | |
200 send_packet_header_buffer[0] = (byte) ((packet_len - 4) >> 24); | |
201 send_packet_header_buffer[1] = (byte) ((packet_len - 4) >> 16); | |
202 send_packet_header_buffer[2] = (byte) ((packet_len - 4) >> 8); | |
203 send_packet_header_buffer[3] = (byte) ((packet_len - 4)); | |
204 send_packet_header_buffer[4] = (byte) padd_len; | |
205 | |
206 cos.write(send_packet_header_buffer, 0, 5); | |
207 cos.write(message, off, len); | |
208 cos.write(send_padding_buffer, 0, padd_len); | |
209 | |
210 if(send_mac != null) { | |
211 send_mac.initMac(send_seq_number); | |
212 send_mac.update(send_packet_header_buffer, 0, 5); | |
213 send_mac.update(message, off, len); | |
214 send_mac.update(send_padding_buffer, 0, padd_len); | |
215 | |
216 send_mac.getMac(send_mac_buffer, 0); | |
217 cos.writePlain(send_mac_buffer, 0, send_mac_buffer.length); | |
218 } | |
219 | |
220 cos.flush(); | |
221 | |
222 if(log.isDebugEnabled()) { | |
223 log.debug("Sent " + Packets.getMessageName(message[off] & 0xff) + " " + len + " bytes payload"); | |
224 } | |
225 | |
226 send_seq_number++; | |
227 } | |
228 | |
229 public int peekNextMessageLength() throws IOException { | |
230 if(recv_packet_header_present == false) { | |
231 cis.read(recv_packet_header_buffer, 0, 5); | |
232 recv_packet_header_present = true; | |
233 } | |
234 | |
235 int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24) | |
236 | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8) | |
237 | ((recv_packet_header_buffer[3] & 0xff)); | |
238 | |
239 int padding_length = recv_packet_header_buffer[4] & 0xff; | |
240 | |
241 if(packet_length > TransportManager.MAX_PACKET_SIZE || packet_length < 12) { | |
242 throw new PacketFormatException(String.format("Illegal packet size (%d)", packet_length)); | |
243 } | |
244 | |
245 int payload_length = packet_length - padding_length - 1; | |
246 | |
247 if(payload_length < 0) { | |
248 throw new PacketFormatException(String.format("Illegal padding_length in packet from remote (%d)", padding_length)); | |
249 } | |
250 | |
251 return payload_length; | |
252 } | |
253 | |
254 public int receiveMessage(byte buffer[], int off, int len) throws IOException { | |
255 if(recv_packet_header_present == false) { | |
256 cis.read(recv_packet_header_buffer, 0, 5); | |
257 } | |
258 else { | |
259 recv_packet_header_present = false; | |
260 } | |
261 | |
262 int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24) | |
263 | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8) | |
264 | ((recv_packet_header_buffer[3] & 0xff)); | |
265 | |
266 int padding_length = recv_packet_header_buffer[4] & 0xff; | |
267 | |
268 if(packet_length > TransportManager.MAX_PACKET_SIZE || packet_length < 12) { | |
269 throw new PacketFormatException(String.format("Illegal packet size (%d)", packet_length)); | |
270 } | |
271 | |
272 int payload_length = packet_length - padding_length - 1; | |
273 | |
274 if(payload_length < 0) { | |
275 throw new PacketFormatException(String.format("Illegal padding_length in packet from remote (%d)", padding_length)); | |
276 } | |
277 | |
278 if(payload_length >= len) { | |
279 throw new IOException("Receive buffer too small (" + len + ", need " + payload_length + ")"); | |
280 } | |
281 | |
282 cis.read(buffer, off, payload_length); | |
283 cis.read(recv_padding_buffer, 0, padding_length); | |
284 | |
285 if(recv_mac != null) { | |
286 cis.readPlain(recv_mac_buffer, 0, recv_mac_buffer.length); | |
287 | |
288 recv_mac.initMac(recv_seq_number); | |
289 recv_mac.update(recv_packet_header_buffer, 0, 5); | |
290 recv_mac.update(buffer, off, payload_length); | |
291 recv_mac.update(recv_padding_buffer, 0, padding_length); | |
292 recv_mac.getMac(recv_mac_buffer_cmp, 0); | |
293 | |
294 for(int i = 0; i < recv_mac_buffer.length; i++) { | |
295 if(recv_mac_buffer[i] != recv_mac_buffer_cmp[i]) { | |
296 throw new IOException("Remote sent corrupt MAC."); | |
297 } | |
298 } | |
299 } | |
300 | |
301 recv_seq_number++; | |
302 | |
303 if(log.isDebugEnabled()) { | |
304 log.debug("Received " + Packets.getMessageName(buffer[off] & 0xff) + " " + payload_length | |
305 + " bytes payload"); | |
306 } | |
307 | |
308 if(recv_comp != null && can_compress) { | |
309 int[] uncomp_len = new int[]{payload_length}; | |
310 buffer = recv_comp.uncompress(buffer, off, uncomp_len); | |
311 return uncomp_len[0]; | |
312 } | |
313 else { | |
314 return payload_length; | |
315 } | |
316 } | |
317 | |
318 public void startCompression() { | |
319 can_compress = true; | |
320 } | |
321 } |