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.security.SecureRandom;
     22 import java.util.Arrays;
     23 import libcore.io.Streams;
     24 import libcore.util.EmptyArray;
     25 
     26 /**
     27  * Represents Client Hello message
     28  * @see <a href="http://www.ietf.org/rfc/rfc2246.txt">TLS 1.0 spec., 7.4.1.2.
     29  * Client hello</a>
     30  *
     31  */
     32 public class ClientHello extends Message {
     33 
     34     /**
     35      * Client version
     36      */
     37     final byte[] client_version;
     38 
     39     /**
     40      * Random bytes
     41      */
     42     final byte[] random = new byte[32];
     43 
     44     /**
     45      * Session id
     46      */
     47     final byte[] session_id;
     48 
     49     /**
     50      * Cipher suites supported by the client
     51      */
     52     final CipherSuite[] cipher_suites;
     53 
     54     /**
     55      * Compression methods supported by the client
     56      */
     57     final byte[] compression_methods;
     58 
     59     /**
     60      * Creates outbound message
     61      * @param sr
     62      * @param version
     63      * @param ses_id
     64      * @param cipher_suite
     65      */
     66     public ClientHello(SecureRandom sr, byte[] version, byte[] ses_id,
     67             CipherSuite[] cipher_suite) {
     68         client_version = version;
     69         long gmt_unix_time = System.currentTimeMillis()/1000;
     70         sr.nextBytes(random);
     71         random[0] = (byte) (gmt_unix_time & 0xFF000000 >>> 24);
     72         random[1] = (byte) (gmt_unix_time & 0xFF0000 >>> 16);
     73         random[2] = (byte) (gmt_unix_time & 0xFF00 >>> 8);
     74         random[3] = (byte) (gmt_unix_time & 0xFF);
     75         session_id = ses_id;
     76         this.cipher_suites = cipher_suite;
     77         compression_methods = new byte[] { 0 }; // CompressionMethod.null
     78         length = 38 + session_id.length + (this.cipher_suites.length << 1)
     79                 + compression_methods.length;
     80     }
     81 
     82     /**
     83      * Creates inbound message
     84      * @param in
     85      * @param length
     86      * @throws IOException
     87      */
     88     public ClientHello(HandshakeIODataStream in, int length) throws IOException {
     89         client_version = new byte[2];
     90         client_version[0] = (byte) in.readUint8();
     91         client_version[1] = (byte) in.readUint8();
     92         Streams.readFully(in, random);
     93         int size = in.read();
     94         session_id = new byte[size];
     95         in.read(session_id, 0, size);
     96         int l = in.readUint16();
     97         if ((l & 0x01) == 0x01) { // cipher suites length must be an even number
     98             fatalAlert(AlertProtocol.DECODE_ERROR,
     99                     "DECODE ERROR: incorrect ClientHello");
    100         }
    101         size = l >> 1;
    102         cipher_suites = new CipherSuite[size];
    103         for (int i = 0; i < size; i++) {
    104             byte b0 = (byte) in.read();
    105             byte b1 = (byte) in.read();
    106             cipher_suites[i] = CipherSuite.getByCode(b0, b1);
    107         }
    108         size = in.read();
    109         compression_methods = new byte[size];
    110         in.read(compression_methods, 0, size);
    111         this.length = 38 + session_id.length + (cipher_suites.length << 1)
    112                 + compression_methods.length;
    113         if (this.length > length) {
    114             fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect ClientHello");
    115         }
    116         // for forward compatibility, extra data is permitted;
    117         // must be ignored
    118         if (this.length < length) {
    119             in.skip(length - this.length);
    120             this.length = length;
    121         }
    122     }
    123     /**
    124      * Parse V2ClientHello
    125      * @param in
    126      * @throws IOException
    127      */
    128     public ClientHello(HandshakeIODataStream in) throws IOException {
    129         if (in.readUint8() != 1) {
    130             fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect V2ClientHello");
    131         }
    132         client_version = new byte[2];
    133         client_version[0] = (byte) in.readUint8();
    134         client_version[1] = (byte) in.readUint8();
    135         int cipher_spec_length = in.readUint16();
    136         if (in.readUint16() != 0) { // session_id_length
    137             // as client already knows the protocol known to a server it should
    138             // initiate the connection in that native protocol
    139             fatalAlert(AlertProtocol.DECODE_ERROR,
    140                     "DECODE ERROR: incorrect V2ClientHello, cannot be used for resuming");
    141         }
    142         int challenge_length = in.readUint16();
    143         if (challenge_length < 16) {
    144             fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect V2ClientHello, short challenge data");
    145         }
    146         session_id = EmptyArray.BYTE;
    147         cipher_suites = new CipherSuite[cipher_spec_length/3];
    148         for (int i = 0; i < cipher_suites.length; i++) {
    149             byte b0 = (byte) in.read();
    150             byte b1 = (byte) in.read();
    151             byte b2 = (byte) in.read();
    152             cipher_suites[i] = CipherSuite.getByCode(b0, b1, b2);
    153         }
    154         compression_methods = new byte[] { 0 }; // CompressionMethod.null
    155 
    156         if (challenge_length < 32) {
    157             Arrays.fill(random, 0, 32 - challenge_length, (byte)0);
    158             System.arraycopy(in.read(challenge_length), 0, random, 32 - challenge_length, challenge_length);
    159         } else if (challenge_length == 32) {
    160             System.arraycopy(in.read(32), 0, random, 0, 32);
    161         } else {
    162             System.arraycopy(in.read(challenge_length), challenge_length - 32, random, 0, 32);
    163         }
    164         if (in.available() > 0) {
    165             fatalAlert(AlertProtocol.DECODE_ERROR, "DECODE ERROR: incorrect V2ClientHello, extra data");
    166         }
    167         this.length = 38 + session_id.length + (cipher_suites.length << 1)
    168                 + compression_methods.length;
    169     }
    170 
    171     /**
    172      * Sends message
    173      * @param out
    174      */
    175     @Override
    176     public void send(HandshakeIODataStream out) {
    177         out.write(client_version);
    178         out.write(random);
    179         out.writeUint8(session_id.length);
    180         out.write(session_id);
    181         int size = cipher_suites.length << 1;
    182         out.writeUint16(size);
    183         for (int i = 0; i < cipher_suites.length; i++) {
    184             out.write(cipher_suites[i].toBytes());
    185         }
    186         out.writeUint8(compression_methods.length);
    187         for (int i = 0; i < compression_methods.length; i++) {
    188             out.write(compression_methods[i]);
    189         }
    190     }
    191 
    192     /**
    193      * Returns client random
    194      * @return client random
    195      */
    196     public byte[] getRandom() {
    197         return random;
    198     }
    199 
    200     /**
    201      * Returns message type
    202      * @return
    203      */
    204     @Override
    205     public int getType() {
    206         return Handshake.CLIENT_HELLO;
    207     }
    208 }
    209