001: package ch.ethz.ssh2.transport;
002:
003: import java.io.IOException;
004: import java.io.InputStream;
005: import java.io.OutputStream;
006: import java.security.SecureRandom;
007:
008: import ch.ethz.ssh2.crypto.cipher.BlockCipher;
009: import ch.ethz.ssh2.crypto.cipher.CipherInputStream;
010: import ch.ethz.ssh2.crypto.cipher.CipherOutputStream;
011: import ch.ethz.ssh2.crypto.cipher.NullCipher;
012: import ch.ethz.ssh2.crypto.digest.MAC;
013: import ch.ethz.ssh2.log.Logger;
014: import ch.ethz.ssh2.packets.Packets;
015:
016: /**
017: * TransportConnection.
018: *
019: * @author Christian Plattner, plattner@inf.ethz.ch
020: * @version $Id: TransportConnection.java,v 1.8 2006/02/14 19:43:15 cplattne Exp $
021: */
022: public class TransportConnection {
023: private static final Logger log = Logger
024: .getLogger(TransportConnection.class);
025:
026: int send_seq_number = 0;
027:
028: int recv_seq_number = 0;
029:
030: CipherInputStream cis;
031:
032: CipherOutputStream cos;
033:
034: boolean useRandomPadding = false;
035:
036: /* Depends on current MAC and CIPHER */
037:
038: MAC send_mac;
039:
040: byte[] send_mac_buffer;
041:
042: int send_padd_blocksize = 8;
043:
044: MAC recv_mac;
045:
046: byte[] recv_mac_buffer;
047:
048: byte[] recv_mac_buffer_cmp;
049:
050: int recv_padd_blocksize = 8;
051:
052: /* won't change */
053:
054: final byte[] send_padding_buffer = new byte[256];
055:
056: final byte[] send_packet_header_buffer = new byte[5];
057:
058: final byte[] recv_padding_buffer = new byte[256];
059:
060: final byte[] recv_packet_header_buffer = new byte[5];
061:
062: boolean recv_packet_header_present = false;
063:
064: ClientServerHello csh;
065:
066: final SecureRandom rnd;
067:
068: public TransportConnection(InputStream is, OutputStream os,
069: SecureRandom rnd) {
070: this .cis = new CipherInputStream(new NullCipher(), is);
071: this .cos = new CipherOutputStream(new NullCipher(), os);
072: this .rnd = rnd;
073: }
074:
075: public void changeRecvCipher(BlockCipher bc, MAC mac) {
076: cis.changeCipher(bc);
077: recv_mac = mac;
078: recv_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
079: recv_mac_buffer_cmp = (mac != null) ? new byte[mac.size()]
080: : null;
081: recv_padd_blocksize = bc.getBlockSize();
082: if (recv_padd_blocksize < 8)
083: recv_padd_blocksize = 8;
084: }
085:
086: public void changeSendCipher(BlockCipher bc, MAC mac) {
087: if ((bc instanceof NullCipher) == false) {
088: /* Only use zero byte padding for the first few packets */
089: useRandomPadding = true;
090: /* Once we start encrypting, there is no way back */
091: }
092:
093: cos.changeCipher(bc);
094: send_mac = mac;
095: send_mac_buffer = (mac != null) ? new byte[mac.size()] : null;
096: send_padd_blocksize = bc.getBlockSize();
097: if (send_padd_blocksize < 8)
098: send_padd_blocksize = 8;
099: }
100:
101: public void sendMessage(byte[] message) throws IOException {
102: sendMessage(message, 0, message.length, 0);
103: }
104:
105: public void sendMessage(byte[] message, int off, int len)
106: throws IOException {
107: sendMessage(message, off, len, 0);
108: }
109:
110: public int getPacketOverheadEstimate() {
111: // return an estimate for the paket overhead (for send operations)
112: return 5 + 4 + (send_padd_blocksize - 1)
113: + send_mac_buffer.length;
114: }
115:
116: public void sendMessage(byte[] message, int off, int len, int padd)
117: throws IOException {
118: if (padd < 4)
119: padd = 4;
120: else if (padd > 64)
121: padd = 64;
122:
123: int packet_len = 5 + len + padd; /* Minimum allowed padding is 4 */
124:
125: int slack = packet_len % send_padd_blocksize;
126:
127: if (slack != 0) {
128: packet_len += (send_padd_blocksize - slack);
129: }
130:
131: if (packet_len < 16)
132: packet_len = 16;
133:
134: int padd_len = packet_len - (5 + len);
135:
136: if (useRandomPadding) {
137: for (int i = 0; i < padd_len; i = i + 4) {
138: /*
139: * don't waste calls to rnd.nextInt() (by using only 8bit of the
140: * output). just believe me: even though we may write here up to 3
141: * bytes which won't be used, there is no "buffer overflow" (i.e.,
142: * arrayindexoutofbounds). the padding buffer is big enough =) (256
143: * bytes, and that is bigger than any current cipher block size + 64).
144: */
145:
146: int r = rnd.nextInt();
147: send_padding_buffer[i] = (byte) r;
148: send_padding_buffer[i + 1] = (byte) (r >> 8);
149: send_padding_buffer[i + 2] = (byte) (r >> 16);
150: send_padding_buffer[i + 3] = (byte) (r >> 24);
151: }
152: } else {
153: /* use zero padding for unencrypted traffic */
154: for (int i = 0; i < padd_len; i++)
155: send_padding_buffer[i] = 0;
156: /* Actually this code is paranoid: we never filled any
157: * bytes into the padding buffer so far, therefore it should
158: * consist of zeros only.
159: */
160: }
161:
162: send_packet_header_buffer[0] = (byte) ((packet_len - 4) >> 24);
163: send_packet_header_buffer[1] = (byte) ((packet_len - 4) >> 16);
164: send_packet_header_buffer[2] = (byte) ((packet_len - 4) >> 8);
165: send_packet_header_buffer[3] = (byte) ((packet_len - 4));
166: send_packet_header_buffer[4] = (byte) padd_len;
167:
168: cos.write(send_packet_header_buffer, 0, 5);
169: cos.write(message, off, len);
170: cos.write(send_padding_buffer, 0, padd_len);
171:
172: if (send_mac != null) {
173: send_mac.initMac(send_seq_number);
174: send_mac.update(send_packet_header_buffer, 0, 5);
175: send_mac.update(message, off, len);
176: send_mac.update(send_padding_buffer, 0, padd_len);
177:
178: send_mac.getMac(send_mac_buffer, 0);
179: cos.writePlain(send_mac_buffer, 0, send_mac_buffer.length);
180: }
181:
182: cos.flush();
183:
184: if (log.isEnabled()) {
185: log.log(90, "Sent "
186: + Packets.getMessageName(message[off] & 0xff) + " "
187: + len + " bytes payload");
188: }
189:
190: send_seq_number++;
191: }
192:
193: public int peekNextMessageLength() throws IOException {
194: if (recv_packet_header_present == false) {
195: cis.read(recv_packet_header_buffer, 0, 5);
196: recv_packet_header_present = true;
197: }
198:
199: int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
200: | ((recv_packet_header_buffer[1] & 0xff) << 16)
201: | ((recv_packet_header_buffer[2] & 0xff) << 8)
202: | ((recv_packet_header_buffer[3] & 0xff));
203:
204: int padding_length = recv_packet_header_buffer[4] & 0xff;
205:
206: if (packet_length > 35000 || packet_length < 12)
207: throw new IOException("Illegal packet size! ("
208: + packet_length + ")");
209:
210: int payload_length = packet_length - padding_length - 1;
211:
212: if (payload_length < 0)
213: throw new IOException(
214: "Illegal padding_length in packet from remote ("
215: + padding_length + ")");
216:
217: return payload_length;
218: }
219:
220: public int receiveMessage(byte buffer[], int off, int len)
221: throws IOException {
222: if (recv_packet_header_present == false) {
223: cis.read(recv_packet_header_buffer, 0, 5);
224: } else
225: recv_packet_header_present = false;
226:
227: int packet_length = ((recv_packet_header_buffer[0] & 0xff) << 24)
228: | ((recv_packet_header_buffer[1] & 0xff) << 16)
229: | ((recv_packet_header_buffer[2] & 0xff) << 8)
230: | ((recv_packet_header_buffer[3] & 0xff));
231:
232: int padding_length = recv_packet_header_buffer[4] & 0xff;
233:
234: if (packet_length > 35000 || packet_length < 12)
235: throw new IOException("Illegal packet size! ("
236: + packet_length + ")");
237:
238: int payload_length = packet_length - padding_length - 1;
239:
240: if (payload_length < 0)
241: throw new IOException(
242: "Illegal padding_length in packet from remote ("
243: + padding_length + ")");
244:
245: if (payload_length >= len)
246: throw new IOException("Receive buffer too small (" + len
247: + ", need " + payload_length + ")");
248:
249: cis.read(buffer, off, payload_length);
250: cis.read(recv_padding_buffer, 0, padding_length);
251:
252: if (recv_mac != null) {
253: cis.readPlain(recv_mac_buffer, 0, recv_mac_buffer.length);
254:
255: recv_mac.initMac(recv_seq_number);
256: recv_mac.update(recv_packet_header_buffer, 0, 5);
257: recv_mac.update(buffer, off, payload_length);
258: recv_mac.update(recv_padding_buffer, 0, padding_length);
259: recv_mac.getMac(recv_mac_buffer_cmp, 0);
260:
261: for (int i = 0; i < recv_mac_buffer.length; i++) {
262: if (recv_mac_buffer[i] != recv_mac_buffer_cmp[i])
263: throw new IOException("Remote sent corrupt MAC.");
264: }
265: }
266:
267: recv_seq_number++;
268:
269: if (log.isEnabled()) {
270: log.log(90, "Received "
271: + Packets.getMessageName(buffer[off] & 0xff) + " "
272: + payload_length + " bytes payload");
273: }
274:
275: return payload_length;
276: }
277: }
|