1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package org.apache.harmony.xnet.provider.jsse; 19 20import org.apache.harmony.xnet.provider.jsse.AlertException; 21import org.apache.harmony.xnet.provider.jsse.SSLInputStream; 22 23import java.io.IOException; 24import java.io.PrintStream; 25import java.security.MessageDigest; 26import java.util.Arrays; 27import javax.net.ssl.SSLHandshakeException; 28 29/** 30 * This class provides Input/Output data functionality 31 * for handshake layer. It provides read and write operations 32 * and accumulates all sent/received handshake's data. 33 * This class can be presented as a combination of 2 data pipes. 34 * The first data pipe is a pipe of income data: append method 35 * places the data at the beginning of the pipe, and read methods 36 * consume the data from the pipe. The second pipe is an outcoming 37 * data pipe: write operations plases the data into the pipe, 38 * and getData methods consume the data. 39 * It is important to note that work with pipe cound not be 40 * started if there is unconsumed data in another pipe. It is 41 * reasoned by the following: handshake protocol performs read 42 * and write operations consecuently. I.e. it first reads all 43 * income data and only than produces the responce and places it 44 * into the stream. 45 * The read operations of the stream presented by the methods 46 * of SSLInputStream which in its turn is an extension of InputStream. 47 * So this stream can be used as an InputStream parameter for 48 * certificate generation. 49 * Also input stream functionality supports marks. The marks 50 * help to reset the position of the stream in case of incompleate 51 * handshake records. Note that in case of exhausting 52 * of income data the EndOfBufferException is thown which implies 53 * the following: 54 * 1. the stream contains scrappy handshake record, 55 * 2. the read position should be reseted to marked, 56 * 3. and more income data is expected. 57 * The throwing of the exception (instead of returning of -1 value 58 * or incompleate filling of destination buffer) 59 * helps to speed up the process of scrappy data recognition and 60 * processing. 61 * For more information about TLS handshake process see 62 * TLS v 1 specification at http://www.ietf.org/rfc/rfc2246.txt. 63 */ 64public class HandshakeIODataStream 65 extends SSLInputStream implements org.apache.harmony.xnet.provider.jsse.Appendable, DataStream { 66 67 // Objects are used to compute digests of data passed 68 // during the handshake phase 69 private static final MessageDigest md5; 70 private static final MessageDigest sha; 71 72 static { 73 try { 74 md5 = MessageDigest.getInstance("MD5"); 75 sha = MessageDigest.getInstance("SHA-1"); 76 } catch (Exception e) { 77 e.printStackTrace(); 78 throw new RuntimeException( 79 "Could not initialize the Digest Algorithms."); 80 } 81 } 82 83 public HandshakeIODataStream() {} 84 85 // buffer is used to keep the handshaking data; 86 private int buff_size = 1024; 87 private int inc_buff_size = 1024; 88 private byte[] buffer = new byte[buff_size]; 89 90 91 // ---------------- Input related functionality ----------------- 92 93 // position of the next byte to read 94 private int read_pos; 95 private int marked_pos; 96 // position of the last byte to read + 1 97 private int read_pos_end; 98 99 @Override 100 public int available() { 101 return read_pos_end - read_pos; 102 } 103 104 @Override 105 public boolean markSupported() { 106 return true; 107 } 108 109 @Override 110 public void mark(int limit) { 111 marked_pos = read_pos; 112 } 113 114 public void mark() { 115 marked_pos = read_pos; 116 } 117 118 @Override 119 public void reset() { 120 read_pos = marked_pos; 121 } 122 123 /** 124 * Removes the data from the marked position to 125 * the current read position. The method is usefull when it is needed 126 * to delete one message from the internal buffer. 127 */ 128 protected void removeFromMarkedPosition() { 129 System.arraycopy(buffer, read_pos, 130 buffer, marked_pos, read_pos_end - read_pos); 131 read_pos_end -= (read_pos - marked_pos); 132 read_pos = marked_pos; 133 } 134 135 /** 136 * read an opaque value; 137 * @param byte: byte 138 * @return 139 */ 140 @Override 141 public int read() throws IOException { 142 if (read_pos == read_pos_end) { 143 //return -1; 144 throw new EndOfBufferException(); 145 } 146 return buffer[read_pos++] & 0xFF; 147 } 148 149 /** 150 * reads vector of opaque values 151 * @param new: long 152 * @return 153 */ 154 @Override 155 public byte[] read(int length) throws IOException { 156 if (length > available()) { 157 throw new EndOfBufferException(); 158 } 159 byte[] res = new byte[length]; 160 System.arraycopy(buffer, read_pos, res, 0, length); 161 read_pos = read_pos + length; 162 return res; 163 } 164 165 @Override 166 public int read(byte[] dest, int offset, int length) throws IOException { 167 if (length > available()) { 168 throw new EndOfBufferException(); 169 } 170 System.arraycopy(buffer, read_pos, dest, offset, length); 171 read_pos = read_pos + length; 172 return length; 173 } 174 175 // ------------------- Extending of the input data --------------------- 176 177 /** 178 * Appends the income data to be read by handshake protocol. 179 * The attempts to overflow the buffer by means of this methods 180 * seem to be futile because of: 181 * 1. The SSL protocol specifies the maximum size of the record 182 * and record protocol does not pass huge messages. 183 * (see TLS v1 specification http://www.ietf.org/rfc/rfc2246.txt , 184 * p 6.2) 185 * 2. After each call of this method, handshake protocol should 186 * start (and starts) the operations on received data and recognize 187 * the fake data if such was provided (to check the size of certificate 188 * for example). 189 */ 190 public void append(byte[] src) { 191 append(src, 0, src.length); 192 } 193 194 private void append(byte[] src, int from, int length) { 195 if (read_pos == read_pos_end) { 196 // start reading state after writing 197 if (write_pos_beg != write_pos) { 198 // error: outboud handshake data was not sent, 199 // but inbound handshake data has been received. 200 throw new AlertException( 201 AlertProtocol.UNEXPECTED_MESSAGE, 202 new SSLHandshakeException( 203 "Handshake message has been received before " 204 + "the last oubound message had been sent.")); 205 } 206 if (read_pos < write_pos) { 207 read_pos = write_pos; 208 read_pos_end = read_pos; 209 } 210 } 211 if (read_pos_end + length > buff_size) { 212 enlargeBuffer(read_pos_end+length-buff_size); 213 } 214 System.arraycopy(src, from, buffer, read_pos_end, length); 215 read_pos_end += length; 216 } 217 218 private void enlargeBuffer(int size) { 219 buff_size = (size < inc_buff_size) 220 ? buff_size + inc_buff_size 221 : buff_size + size; 222 byte[] new_buff = new byte[buff_size]; 223 System.arraycopy(buffer, 0, new_buff, 0, buffer.length); 224 buffer = new_buff; 225 } 226 227 protected void clearBuffer() { 228 read_pos = 0; 229 marked_pos = 0; 230 read_pos_end = 0; 231 write_pos = 0; 232 write_pos_beg = 0; 233 Arrays.fill(buffer, (byte) 0); 234 } 235 236 // ------------------- Output related functionality -------------------- 237 238 // position in the buffer available for write 239 private int write_pos; 240 // position in the buffer where the last write session has begun 241 private int write_pos_beg; 242 243 // checks if the data can be written in the buffer 244 private void check(int length) { 245 // (write_pos == write_pos_beg) iff: 246 // 1. there were not write operations yet 247 // 2. all written data was demanded by getData methods 248 if (write_pos == write_pos_beg) { 249 // just started to write after the reading 250 if (read_pos != read_pos_end) { 251 // error: attempt to write outbound data into the stream before 252 // all the inbound handshake data had been read 253 throw new AlertException( 254 AlertProtocol.INTERNAL_ERROR, 255 new SSLHandshakeException("Data was not fully read: " 256 + read_pos + " " + read_pos_end)); 257 } 258 // set up the write positions 259 if (write_pos_beg < read_pos_end) { 260 write_pos_beg = read_pos_end; 261 write_pos = write_pos_beg; 262 } 263 } 264 // if there is not enought free space in the buffer - enlarge it: 265 if (write_pos + length >= buff_size) { 266 enlargeBuffer(length); 267 } 268 } 269 270 /** 271 * Writes an opaque value 272 * @param byte: byte 273 */ 274 public void write(byte b) { 275 check(1); 276 buffer[write_pos++] = b; 277 } 278 279 /** 280 * Writes Uint8 value 281 * @param long: the value to be written (last byte) 282 */ 283 public void writeUint8(long n) { 284 check(1); 285 buffer[write_pos++] = (byte) (n & 0x00ff); 286 } 287 288 /** 289 * Writes Uint16 value 290 * @param long: the value to be written (last 2 bytes) 291 */ 292 public void writeUint16(long n) { 293 check(2); 294 buffer[write_pos++] = (byte) ((n & 0x00ff00) >> 8); 295 buffer[write_pos++] = (byte) (n & 0x00ff); 296 } 297 298 /** 299 * Writes Uint24 value 300 * @param long: the value to be written (last 3 bytes) 301 */ 302 public void writeUint24(long n) { 303 check(3); 304 buffer[write_pos++] = (byte) ((n & 0x00ff0000) >> 16); 305 buffer[write_pos++] = (byte) ((n & 0x00ff00) >> 8); 306 buffer[write_pos++] = (byte) (n & 0x00ff); 307 } 308 309 /** 310 * Writes Uint32 value 311 * @param long: the value to be written (last 4 bytes) 312 */ 313 public void writeUint32(long n) { 314 check(4); 315 buffer[write_pos++] = (byte) ((n & 0x00ff000000) >> 24); 316 buffer[write_pos++] = (byte) ((n & 0x00ff0000) >> 16); 317 buffer[write_pos++] = (byte) ((n & 0x00ff00) >> 8); 318 buffer[write_pos++] = (byte) (n & 0x00ff); 319 } 320 321 /** 322 * Writes Uint64 value 323 * @param long: the value to be written 324 */ 325 public void writeUint64(long n) { 326 check(8); 327 buffer[write_pos++] = (byte) ((n & 0x00ff00000000000000L) >> 56); 328 buffer[write_pos++] = (byte) ((n & 0x00ff000000000000L) >> 48); 329 buffer[write_pos++] = (byte) ((n & 0x00ff0000000000L) >> 40); 330 buffer[write_pos++] = (byte) ((n & 0x00ff00000000L) >> 32); 331 buffer[write_pos++] = (byte) ((n & 0x00ff000000) >> 24); 332 buffer[write_pos++] = (byte) ((n & 0x00ff0000) >> 16); 333 buffer[write_pos++] = (byte) ((n & 0x00ff00) >> 8); 334 buffer[write_pos++] = (byte) (n & 0x00ff); 335 } 336 337 /** 338 * writes vector of opaque values 339 * @param vector the vector to be written 340 */ 341 public void write(byte[] vector) { 342 check(vector.length); 343 System.arraycopy(vector, 0, buffer, write_pos, vector.length); 344 write_pos += vector.length; 345 } 346 347 // ------------------- Retrieve the written bytes ---------------------- 348 349 public boolean hasData() { 350 return (write_pos > write_pos_beg); 351 } 352 353 /** 354 * returns the chunk of stored data with the length no more than specified. 355 * @param length: int 356 * @return 357 */ 358 public byte[] getData(int length) { 359 byte[] res; 360 if (write_pos - write_pos_beg < length) { 361 res = new byte[write_pos - write_pos_beg]; 362 System.arraycopy(buffer, write_pos_beg, 363 res, 0, write_pos-write_pos_beg); 364 write_pos_beg = write_pos; 365 } else { 366 res = new byte[length]; 367 System.arraycopy(buffer, write_pos_beg, res, 0, length); 368 write_pos_beg += length; 369 } 370 return res; 371 } 372 373 // ---------------------- Debud functionality ------------------------- 374 375 protected void printContent(PrintStream outstream) { 376 int perLine = 20; 377 String prefix = " "; 378 String delimiter = ""; 379 380 for (int i=write_pos_beg; i<write_pos; i++) { 381 String tail = Integer.toHexString( 382 0x00ff & buffer[i]).toUpperCase(); 383 if (tail.length() == 1) { 384 tail = "0" + tail; 385 } 386 outstream.print(prefix + tail + delimiter); 387 388 if (((i-write_pos_beg+1)%10) == 0) { 389 outstream.print(" "); 390 } 391 392 if (((i-write_pos_beg+1)%perLine) == 0) { 393 outstream.println(); 394 } 395 } 396 outstream.println(); 397 } 398 399 // ---------------------- Message Digest Functionality ---------------- 400 401 /** 402 * Returns the MD5 digest of the data passed throught the stream 403 * @return MD5 digest 404 */ 405 protected byte[] getDigestMD5() { 406 synchronized (md5) { 407 int len = (read_pos_end > write_pos) 408 ? read_pos_end 409 : write_pos; 410 md5.update(buffer, 0, len); 411 return md5.digest(); 412 } 413 } 414 415 /** 416 * Returns the SHA-1 digest of the data passed throught the stream 417 * @return SHA-1 digest 418 */ 419 protected byte[] getDigestSHA() { 420 synchronized (sha) { 421 int len = (read_pos_end > write_pos) 422 ? read_pos_end 423 : write_pos; 424 sha.update(buffer, 0, len); 425 return sha.digest(); 426 } 427 } 428 429 /** 430 * Returns the MD5 digest of the data passed throught the stream 431 * except last message 432 * @return MD5 digest 433 */ 434 protected byte[] getDigestMD5withoutLast() { 435 synchronized (md5) { 436 md5.update(buffer, 0, marked_pos); 437 return md5.digest(); 438 } 439 } 440 441 /** 442 * Returns the SHA-1 digest of the data passed throught the stream 443 * except last message 444 * @return SHA-1 digest 445 */ 446 protected byte[] getDigestSHAwithoutLast() { 447 synchronized (sha) { 448 sha.update(buffer, 0, marked_pos); 449 return sha.digest(); 450 } 451 } 452 453 /** 454 * Returns all the data passed throught the stream 455 * @return all the data passed throught the stream at the moment 456 */ 457 protected byte[] getMessages() { 458 int len = (read_pos_end > write_pos) ? read_pos_end : write_pos; 459 byte[] res = new byte[len]; 460 System.arraycopy(buffer, 0, res, 0, len); 461 return res; 462 } 463} 464 465