Home | History | Annotate | Download | only in jsse
      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 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.apache.harmony.xnet.provider.jsse.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