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