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