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