OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / libcore / luni / src / main / java / org / apache / harmony / xnet / provider / jsse / HandshakeIODataStream.java
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 }