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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 package org.apache.harmony.xnet.provider.jsse;
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;
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
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
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
58 * For more information about TLS handshake process see
59 * TLS v 1 specification at http://www.ietf.org/rfc/rfc2246.txt.
61 public class HandshakeIODataStream
62 extends SSLInputStream implements org.apache.harmony.xnet.provider.jsse.Appendable, DataStream {
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;
71 md5 = MessageDigest.getInstance("MD5");
72 sha = MessageDigest.getInstance("SHA-1");
73 } catch (Exception e) {
75 throw new RuntimeException(
76 "Could not initialize the Digest Algorithms.");
80 public HandshakeIODataStream() {}
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];
88 // ---------------- Input related functionality -----------------
90 // position of the next byte to read
92 private int marked_pos;
93 // position of the last byte to read + 1
94 private int read_pos_end;
97 public int available() {
98 return read_pos_end - read_pos;
102 public boolean markSupported() {
107 public void mark(int limit) {
108 marked_pos = read_pos;
112 marked_pos = read_pos;
116 public void reset() {
117 read_pos = marked_pos;
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.
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;
133 * read an opaque value;
138 public int read() throws IOException {
139 if (read_pos == read_pos_end) {
141 throw new EndOfBufferException();
143 return buffer[read_pos++] & 0xFF;
147 * reads vector of opaque values
152 public byte[] read(int length) throws IOException {
153 if (length > available()) {
154 throw new EndOfBufferException();
156 byte[] res = new byte[length];
157 System.arraycopy(buffer, read_pos, res, 0, length);
158 read_pos = read_pos + length;
163 public int read(byte[] dst, int offset, int length) throws IOException {
164 if (length > available()) {
165 throw new EndOfBufferException();
167 System.arraycopy(buffer, read_pos, dst, offset, length);
168 read_pos = read_pos + length;
172 // ------------------- Extending of the input data ---------------------
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 ,
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
187 public void append(byte[] src) {
188 append(src, 0, src.length);
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."));
203 if (read_pos < write_pos) {
204 read_pos = write_pos;
205 read_pos_end = read_pos;
208 if (read_pos_end + length > buff_size) {
209 enlargeBuffer(read_pos_end+length-buff_size);
211 System.arraycopy(src, from, buffer, read_pos_end, length);
212 read_pos_end += length;
215 private void enlargeBuffer(int size) {
216 buff_size = (size < inc_buff_size)
217 ? buff_size + inc_buff_size
219 byte[] new_buff = new byte[buff_size];
220 System.arraycopy(buffer, 0, new_buff, 0, buffer.length);
224 protected void clearBuffer() {
230 Arrays.fill(buffer, (byte) 0);
233 // ------------------- Output related functionality --------------------
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;
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));
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;
261 // if there is not enought free space in the buffer - enlarge it:
262 if (write_pos + length >= buff_size) {
263 enlargeBuffer(length);
268 * Writes an opaque value
271 public void write(byte b) {
273 buffer[write_pos++] = b;
278 * @param long: the value to be written (last byte)
280 public void writeUint8(long n) {
282 buffer[write_pos++] = (byte) (n & 0x00ff);
286 * Writes Uint16 value
287 * @param long: the value to be written (last 2 bytes)
289 public void writeUint16(long n) {
291 buffer[write_pos++] = (byte) ((n & 0x00ff00) >> 8);
292 buffer[write_pos++] = (byte) (n & 0x00ff);
296 * Writes Uint24 value
297 * @param long: the value to be written (last 3 bytes)
299 public void writeUint24(long n) {
301 buffer[write_pos++] = (byte) ((n & 0x00ff0000) >> 16);
302 buffer[write_pos++] = (byte) ((n & 0x00ff00) >> 8);
303 buffer[write_pos++] = (byte) (n & 0x00ff);
307 * Writes Uint32 value
308 * @param long: the value to be written (last 4 bytes)
310 public void writeUint32(long n) {
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);
319 * Writes Uint64 value
320 * @param long: the value to be written
322 public void writeUint64(long n) {
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);
335 * writes vector of opaque values
336 * @param vector the vector to be written
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;
344 // ------------------- Retrieve the written bytes ----------------------
346 public boolean hasData() {
347 return (write_pos > write_pos_beg);
351 * returns the chunk of stored data with the length no more than specified.
355 public byte[] getData(int length) {
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;
363 res = new byte[length];
364 System.arraycopy(buffer, write_pos_beg, res, 0, length);
365 write_pos_beg += length;
370 // ---------------------- Debud functionality -------------------------
372 protected void printContent(PrintStream outstream) {
375 String delimiter = "";
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) {
383 outstream.print(prefix + tail + delimiter);
385 if (((i-write_pos_beg+1)%10) == 0) {
386 outstream.print(" ");
389 if (((i-write_pos_beg+1)%perLine) == 0) {
396 // ---------------------- Message Digest Functionality ----------------
399 * Returns the MD5 digest of the data passed throught the stream
402 protected byte[] getDigestMD5() {
404 int len = (read_pos_end > write_pos)
407 md5.update(buffer, 0, len);
413 * Returns the SHA-1 digest of the data passed throught the stream
414 * @return SHA-1 digest
416 protected byte[] getDigestSHA() {
418 int len = (read_pos_end > write_pos)
421 sha.update(buffer, 0, len);
427 * Returns the MD5 digest of the data passed throught the stream
428 * except last message
431 protected byte[] getDigestMD5withoutLast() {
433 md5.update(buffer, 0, marked_pos);
439 * Returns the SHA-1 digest of the data passed throught the stream
440 * except last message
441 * @return SHA-1 digest
443 protected byte[] getDigestSHAwithoutLast() {
445 sha.update(buffer, 0, marked_pos);
451 * Returns all the data passed throught the stream
452 * @return all the data passed throught the stream at the moment
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);