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