comparison src/ch/ethz/ssh2/transport/TransportConnection.java @ 342:175c7d68f3c4

merge ganymed into mainline
author Carl Byington <carl@five-ten-sg.com>
date Thu, 31 Jul 2014 16:33:38 -0700
parents 071eccdff8ea
children 20d0a1356c43
comparison
equal deleted inserted replaced
272:ce2f4e397703 342:175c7d68f3c4
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
96 if (recv_padd_blocksize < 8) {
97 recv_padd_blocksize = 8;
98 }
99 }
100
101 public void changeSendCipher(BlockCipher bc, MAC mac) {
102 if ((bc instanceof NullCipher) == false) {
103 /* Only use zero byte padding for the first few packets */
104 useRandomPadding = true;
105 /* Once we start encrypting, there is no way back */
106 }
107
108 cos.changeCipher(bc);
109 send_mac = mac;
110 send_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
111 send_padd_blocksize = bc.getBlockSize();
112
113 if (send_padd_blocksize < 8) {
114 send_padd_blocksize = 8;
115 }
116 }
117
118 public void changeRecvCompression(Compressor comp) {
119 recv_comp = comp;
120
121 if (comp != null) {
122 recv_comp_buffer = new byte[comp.getBufferSize()];
123 }
124 }
125
126 public void changeSendCompression(Compressor comp) {
127 send_comp = comp;
128
129 if (comp != null) {
130 send_comp_buffer = new byte[comp.getBufferSize()];
131 }
132 }
133
134 public void sendMessage(byte[] message) throws IOException {
135 sendMessage(message, 0, message.length, 0);
136 }
137
138 public void sendMessage(byte[] message, int off, int len) throws IOException {
139 sendMessage(message, off, len, 0);
140 }
141
142 public int getPacketOverheadEstimate() {
143 // return an estimate for the paket overhead (for send operations)
144 return 5 + 4 + (send_padd_blocksize - 1) + send_mac_buffer.length;
145 }
146
147 public void sendMessage(byte[] message, int off, int len, int padd) throws IOException {
148 if (padd < 4) {
149 padd = 4;
150 }
151 else if (padd > 64) {
152 padd = 64;
153 }
154
155 if (send_comp != null && can_compress) {
156 len = send_comp.compress(message, off, len, send_comp_buffer);
157 message = send_comp_buffer;
158 }
159
160 int packet_len = 5 + len + padd; /* Minimum allowed padding is 4 */
161 int slack = packet_len % send_padd_blocksize;
162
163 if (slack != 0) {
164 packet_len += (send_padd_blocksize - slack);
165 }
166
167 if (packet_len < 16) {
168 packet_len = 16;
169 }
170
171 int padd_len = packet_len - (5 + len);
172
173 if (useRandomPadding) {
174 for (int i = 0; i < padd_len; i = i + 4) {
175 /*
176 * don't waste calls to rnd.nextInt() (by using only 8bit of the
177 * output). just believe me: even though we may write here up to 3
178 * bytes which won't be used, there is no "buffer overflow" (i.e.,
179 * arrayindexoutofbounds). the padding buffer is big enough =) (256
180 * bytes, and that is bigger than any current cipher block size + 64).
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
195 /* Actually this code is paranoid: we never filled any
196 * bytes into the padding buffer so far, therefore it should
197 * consist of zeros only.
198 */
199 }
200
201 send_packet_header_buffer[0] = (byte)((packet_len - 4) >> 24);
202 send_packet_header_buffer[1] = (byte)((packet_len - 4) >> 16);
203 send_packet_header_buffer[2] = (byte)((packet_len - 4) >> 8);
204 send_packet_header_buffer[3] = (byte)((packet_len - 4));
205 send_packet_header_buffer[4] = (byte) padd_len;
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 send_mac.getMac(send_mac_buffer, 0);
216 cos.writePlain(send_mac_buffer, 0, send_mac_buffer.length);
217 }
218
219 cos.flush();
220
221 if (log.isDebugEnabled()) {
222 log.debug("Sent " + Packets.getMessageName(message[off] & 0xff) + " " + len + " bytes payload");
223 }
224
225 send_seq_number++;
226 }
227
228 public int peekNextMessageLength() throws IOException {
229 if (recv_packet_header_present == false) {
230 cis.read(recv_packet_header_buffer, 0, 5);
231 recv_packet_header_present = true;
232 }
233
234 int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
235 | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
236 | ((recv_packet_header_buffer[3] & 0xff));
237 int padding_length = recv_packet_header_buffer[4] & 0xff;
238
239 if (packet_length > TransportManager.MAX_PACKET_SIZE || packet_length < 12) {
240 throw new PacketFormatException(String.format("Illegal packet size (%d)", packet_length));
241 }
242
243 int payload_length = packet_length - padding_length - 1;
244
245 if (payload_length < 0) {
246 throw new PacketFormatException(String.format("Illegal padding_length in packet from remote (%d)", padding_length));
247 }
248
249 return payload_length;
250 }
251
252 public int receiveMessage(byte buffer[], int off, int len) throws IOException {
253 if (recv_packet_header_present == false) {
254 cis.read(recv_packet_header_buffer, 0, 5);
255 }
256 else {
257 recv_packet_header_present = false;
258 }
259
260 int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
261 | ((recv_packet_header_buffer[1] & 0xff) << 16) | ((recv_packet_header_buffer[2] & 0xff) << 8)
262 | ((recv_packet_header_buffer[3] & 0xff));
263 int padding_length = recv_packet_header_buffer[4] & 0xff;
264
265 if (packet_length > TransportManager.MAX_PACKET_SIZE || packet_length < 12) {
266 throw new PacketFormatException(String.format("Illegal packet size (%d)", packet_length));
267 }
268
269 int payload_length = packet_length - padding_length - 1;
270
271 if (payload_length < 0) {
272 throw new PacketFormatException(String.format("Illegal padding_length in packet from remote (%d)", padding_length));
273 }
274
275 if (payload_length >= len) {
276 throw new IOException("Receive buffer too small (" + len + ", need " + payload_length + ")");
277 }
278
279 cis.read(buffer, off, payload_length);
280 cis.read(recv_padding_buffer, 0, padding_length);
281
282 if (recv_mac != null) {
283 cis.readPlain(recv_mac_buffer, 0, recv_mac_buffer.length);
284 recv_mac.initMac(recv_seq_number);
285 recv_mac.update(recv_packet_header_buffer, 0, 5);
286 recv_mac.update(buffer, off, payload_length);
287 recv_mac.update(recv_padding_buffer, 0, padding_length);
288 recv_mac.getMac(recv_mac_buffer_cmp, 0);
289
290 for (int i = 0; i < recv_mac_buffer.length; i++) {
291 if (recv_mac_buffer[i] != recv_mac_buffer_cmp[i]) {
292 throw new IOException("Remote sent corrupt MAC.");
293 }
294 }
295 }
296
297 recv_seq_number++;
298
299 if (log.isDebugEnabled()) {
300 log.debug("Received " + Packets.getMessageName(buffer[off] & 0xff) + " " + payload_length
301 + " bytes payload");
302 }
303
304 if (recv_comp != null && can_compress) {
305 int[] uncomp_len = new int[] {payload_length};
306 buffer = recv_comp.uncompress(buffer, off, uncomp_len);
307 return uncomp_len[0];
308 }
309 else {
310 return payload_length;
311 }
312 }
313
314 public void startCompression() {
315 can_compress = true;
316 }
317 }