001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /**
019: * @author Alexander Y. Kleymenov
020: * @version $Revision$
021: */package org.apache.harmony.xnet.provider.jsse;
022:
023: import org.apache.harmony.xnet.provider.jsse.AlertException;
024: import org.apache.harmony.xnet.provider.jsse.SSLInputStream;
025:
026: import java.io.IOException;
027: import java.io.PrintStream;
028: import java.security.MessageDigest;
029: import java.util.Arrays;
030: import javax.net.ssl.SSLHandshakeException;
031:
032: /**
033: * This class provides Input/Output data functionality
034: * for handshake layer. It provides read and write operations
035: * and accumulates all sent/received handshake's data.
036: * This class can be presented as a combination of 2 data pipes.
037: * The first data pipe is a pipe of income data: append method
038: * places the data at the beginning of the pipe, and read methods
039: * consume the data from the pipe. The second pipe is an outcoming
040: * data pipe: write operations plases the data into the pipe,
041: * and getData methods consume the data.
042: * It is important to note that work with pipe cound not be
043: * started if there is unconsumed data in another pipe. It is
044: * reasoned by the following: handshake protocol performs read
045: * and write operations consecuently. I.e. it first reads all
046: * income data and only than produces the responce and places it
047: * into the stream.
048: * The read operations of the stream presented by the methods
049: * of SSLInputStream which in its turn is an extension of InputStream.
050: * So this stream can be used as an InputStream parameter for
051: * certificate generation.
052: * Also input stream functionality supports marks. The marks
053: * help to reset the position of the stream in case of incompleate
054: * handshake records. Note that in case of exhausting
055: * of income data the EndOfBufferException is thown which implies
056: * the following:
057: * 1. the stream contains scrappy handshake record,
058: * 2. the read position should be reseted to marked,
059: * 3. and more income data is expected.
060: * The throwing of the exception (instead of returning of -1 value
061: * or incompleate filling of destination buffer)
062: * helps to speed up the process of scrappy data recognition and
063: * processing.
064: * For more information about TLS handshake process see
065: * TLS v 1 specification at http://www.ietf.org/rfc/rfc2246.txt.
066: */
067: public class HandshakeIODataStream extends SSLInputStream implements
068: org.apache.harmony.xnet.provider.jsse.Appendable, DataStream {
069:
070: // Objects are used to compute digests of data passed
071: // during the handshake phase
072: private static final MessageDigest md5;
073: private static final MessageDigest sha;
074:
075: static {
076: try {
077: md5 = MessageDigest.getInstance("MD5");
078: sha = MessageDigest.getInstance("SHA-1");
079: } catch (Exception e) {
080: e.printStackTrace();
081: throw new RuntimeException(
082: "Could not initialize the Digest Algorithms.");
083: }
084: }
085:
086: public HandshakeIODataStream() {
087: }
088:
089: // buffer is used to keep the handshaking data;
090: private int buff_size = 1024;
091: private int inc_buff_size = 1024;
092: private byte[] buffer = new byte[buff_size];
093:
094: // ---------------- Input related functionality -----------------
095:
096: // position of the next byte to read
097: private int read_pos;
098: private int marked_pos;
099: // position of the last byte to read + 1
100: private int read_pos_end;
101:
102: public int available() {
103: return read_pos_end - read_pos;
104: }
105:
106: public boolean markSupported() {
107: return true;
108: }
109:
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: public void reset() {
119: read_pos = marked_pos;
120: }
121:
122: /**
123: * Removes the data from the marked position to
124: * the current read position. The method is usefull when it is needed
125: * to delete one message from the internal buffer.
126: */
127: protected void removeFromMarkedPosition() {
128: System.arraycopy(buffer, read_pos, buffer, marked_pos,
129: read_pos_end - read_pos);
130: read_pos_end -= (read_pos - marked_pos);
131: read_pos = marked_pos;
132: }
133:
134: /**
135: * read an opaque value;
136: * @param byte: byte
137: * @return
138: */
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: 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: public int read(byte[] dest, int offset, int length)
163: throws IOException {
164: if (length > available()) {
165: throw new EndOfBufferException();
166: }
167: System.arraycopy(buffer, read_pos, dest, 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) ? buff_size + inc_buff_size
217: : buff_size + size;
218: byte[] new_buff = new byte[buff_size];
219: System.arraycopy(buffer, 0, new_buff, 0, buffer.length);
220: buffer = new_buff;
221: }
222:
223: protected void clearBuffer() {
224: read_pos = 0;
225: marked_pos = 0;
226: read_pos_end = 0;
227: write_pos = 0;
228: write_pos_beg = 0;
229: Arrays.fill(buffer, (byte) 0);
230: }
231:
232: // ------------------- Output related functionality --------------------
233:
234: // position in the buffer available for write
235: private int write_pos;
236: // position in the buffer where the last write session has begun
237: private int write_pos_beg;
238:
239: // checks if the data can be written in the buffer
240: private void check(int length) {
241: // (write_pos == write_pos_beg) iff:
242: // 1. there were not write operations yet
243: // 2. all written data was demanded by getData methods
244: if (write_pos == write_pos_beg) {
245: // just started to write after the reading
246: if (read_pos != read_pos_end) {
247: // error: attempt to write outbound data into the stream before
248: // all the inbound handshake data had been read
249: throw new AlertException(AlertProtocol.INTERNAL_ERROR,
250: new SSLHandshakeException(
251: "Data was not fully read: " + read_pos
252: + " " + read_pos_end));
253: }
254: // set up the write positions
255: if (write_pos_beg < read_pos_end) {
256: write_pos_beg = read_pos_end;
257: write_pos = write_pos_beg;
258: }
259: }
260: // if there is not enought free space in the buffer - enlarge it:
261: if (write_pos + length >= buff_size) {
262: enlargeBuffer(length);
263: }
264: }
265:
266: /**
267: * Writes an opaque value
268: * @param byte: byte
269: */
270: public void write(byte b) {
271: check(1);
272: buffer[write_pos++] = b;
273: }
274:
275: /**
276: * Writes Uint8 value
277: * @param long: the value to be written (last byte)
278: */
279: public void writeUint8(long n) {
280: check(1);
281: buffer[write_pos++] = (byte) (n & 0x00ff);
282: }
283:
284: /**
285: * Writes Uint16 value
286: * @param long: the value to be written (last 2 bytes)
287: */
288: public void writeUint16(long n) {
289: check(2);
290: buffer[write_pos++] = (byte) ((n & 0x00ff00) >> 8);
291: buffer[write_pos++] = (byte) (n & 0x00ff);
292: }
293:
294: /**
295: * Writes Uint24 value
296: * @param long: the value to be written (last 3 bytes)
297: */
298: public void writeUint24(long n) {
299: check(3);
300: buffer[write_pos++] = (byte) ((n & 0x00ff0000) >> 16);
301: buffer[write_pos++] = (byte) ((n & 0x00ff00) >> 8);
302: buffer[write_pos++] = (byte) (n & 0x00ff);
303: }
304:
305: /**
306: * Writes Uint32 value
307: * @param long: the value to be written (last 4 bytes)
308: */
309: public void writeUint32(long n) {
310: check(4);
311: buffer[write_pos++] = (byte) ((n & 0x00ff000000) >> 24);
312: buffer[write_pos++] = (byte) ((n & 0x00ff0000) >> 16);
313: buffer[write_pos++] = (byte) ((n & 0x00ff00) >> 8);
314: buffer[write_pos++] = (byte) (n & 0x00ff);
315: }
316:
317: /**
318: * Writes Uint64 value
319: * @param long: the value to be written
320: */
321: public void writeUint64(long n) {
322: check(8);
323: buffer[write_pos++] = (byte) ((n & 0x00ff00000000000000L) >> 56);
324: buffer[write_pos++] = (byte) ((n & 0x00ff000000000000L) >> 48);
325: buffer[write_pos++] = (byte) ((n & 0x00ff0000000000L) >> 40);
326: buffer[write_pos++] = (byte) ((n & 0x00ff00000000L) >> 32);
327: buffer[write_pos++] = (byte) ((n & 0x00ff000000) >> 24);
328: buffer[write_pos++] = (byte) ((n & 0x00ff0000) >> 16);
329: buffer[write_pos++] = (byte) ((n & 0x00ff00) >> 8);
330: buffer[write_pos++] = (byte) (n & 0x00ff);
331: }
332:
333: /**
334: * writes vector of opaque values
335: * @param vector the vector to be written
336: */
337: public void write(byte[] vector) {
338: check(vector.length);
339: System.arraycopy(vector, 0, buffer, write_pos, vector.length);
340: write_pos += vector.length;
341: }
342:
343: // ------------------- Retrieve the written bytes ----------------------
344:
345: public boolean hasData() {
346: return (write_pos > write_pos_beg);
347: }
348:
349: /**
350: * returns the chunk of stored data with the length no more than specified.
351: * @param length: int
352: * @return
353: */
354: public byte[] getData(int length) {
355: byte[] res;
356: if (write_pos - write_pos_beg < length) {
357: res = new byte[write_pos - write_pos_beg];
358: System.arraycopy(buffer, write_pos_beg, res, 0, write_pos
359: - write_pos_beg);
360: write_pos_beg = write_pos;
361: } else {
362: res = new byte[length];
363: System.arraycopy(buffer, write_pos_beg, res, 0, length);
364: write_pos_beg += length;
365: }
366: return res;
367: }
368:
369: // ---------------------- Debud functionality -------------------------
370:
371: protected void printContent(PrintStream outstream) {
372: int perLine = 20;
373: String prefix = " ";
374: String delimiter = "";
375:
376: for (int i = write_pos_beg; i < write_pos; i++) {
377: String tail = Integer.toHexString(0x00ff & buffer[i])
378: .toUpperCase();
379: if (tail.length() == 1) {
380: tail = "0" + tail;
381: }
382: outstream.print(prefix + tail + delimiter);
383:
384: if (((i - write_pos_beg + 1) % 10) == 0) {
385: outstream.print(" ");
386: }
387:
388: if (((i - write_pos_beg + 1) % perLine) == 0) {
389: outstream.println();
390: }
391: }
392: outstream.println();
393: }
394:
395: // ---------------------- Message Digest Functionality ----------------
396:
397: /**
398: * Returns the MD5 digest of the data passed throught the stream
399: * @return MD5 digest
400: */
401: protected byte[] getDigestMD5() {
402: synchronized (md5) {
403: int len = (read_pos_end > write_pos) ? read_pos_end
404: : write_pos;
405: md5.update(buffer, 0, len);
406: return md5.digest();
407: }
408: }
409:
410: /**
411: * Returns the SHA-1 digest of the data passed throught the stream
412: * @return SHA-1 digest
413: */
414: protected byte[] getDigestSHA() {
415: synchronized (sha) {
416: int len = (read_pos_end > write_pos) ? read_pos_end
417: : write_pos;
418: sha.update(buffer, 0, len);
419: return sha.digest();
420: }
421: }
422:
423: /**
424: * Returns the MD5 digest of the data passed throught the stream
425: * except last message
426: * @return MD5 digest
427: */
428: protected byte[] getDigestMD5withoutLast() {
429: synchronized (md5) {
430: md5.update(buffer, 0, marked_pos);
431: return md5.digest();
432: }
433: }
434:
435: /**
436: * Returns the SHA-1 digest of the data passed throught the stream
437: * except last message
438: * @return SHA-1 digest
439: */
440: protected byte[] getDigestSHAwithoutLast() {
441: synchronized (sha) {
442: sha.update(buffer, 0, marked_pos);
443: return sha.digest();
444: }
445: }
446:
447: /**
448: * Returns all the data passed throught the stream
449: * @return all the data passed throught the stream at the moment
450: */
451: protected byte[] getMessages() {
452: int len = (read_pos_end > write_pos) ? read_pos_end : write_pos;
453: byte[] res = new byte[len];
454: System.arraycopy(buffer, 0, res, 0, len);
455: return res;
456: }
457: }
|