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 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 }
    461