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