001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /**
019: * @author Boris Kuznetsov
020: * @version $Revision$
021: */package org.apache.harmony.xnet.provider.jsse;
022:
023: import java.io.IOException;
024: import java.security.SecureRandom;
025: import java.util.Arrays;
026:
027: /**
028: *
029: * Represents Client Hello message
030: * @see TLS 1.0 spec., 7.4.1.2. Client hello
031: * (http://www.ietf.org/rfc/rfc2246.txt)
032: *
033: */
034: public class ClientHello extends Message {
035:
036: /**
037: * Client version
038: */
039: final byte[] client_version;
040:
041: /**
042: * Random bytes
043: */
044: final byte[] random = new byte[32];
045:
046: /**
047: * Session id
048: */
049: final byte[] session_id;
050:
051: /**
052: * Cipher suites supported by the client
053: */
054: final CipherSuite[] cipher_suites;
055:
056: /**
057: * Compression methods supported by the client
058: */
059: final byte[] compression_methods;
060:
061: /**
062: * Creates outbound message
063: * @param sr
064: * @param version
065: * @param ses_id
066: * @param cipher_suite
067: */
068: public ClientHello(SecureRandom sr, byte[] version, byte[] ses_id,
069: CipherSuite[] cipher_suite) {
070: client_version = version;
071: long gmt_unix_time = System.currentTimeMillis() / 1000;
072: sr.nextBytes(random);
073: random[0] = (byte) (gmt_unix_time & 0xFF000000 >>> 24);
074: random[1] = (byte) (gmt_unix_time & 0xFF0000 >>> 16);
075: random[2] = (byte) (gmt_unix_time & 0xFF00 >>> 8);
076: random[3] = (byte) (gmt_unix_time & 0xFF);
077: session_id = ses_id;
078: this .cipher_suites = cipher_suite;
079: compression_methods = new byte[] { 0 }; // CompressionMethod.null
080: length = 38 + session_id.length
081: + (this .cipher_suites.length << 1)
082: + compression_methods.length;
083: }
084:
085: /**
086: * Creates inbound message
087: * @param in
088: * @param length
089: * @throws IOException
090: */
091: public ClientHello(HandshakeIODataStream in, int length)
092: throws IOException {
093: client_version = new byte[2];
094: client_version[0] = (byte) in.readUint8();
095: client_version[1] = (byte) in.readUint8();
096: in.read(random, 0, 32);
097: int size = in.read();
098: session_id = new byte[size];
099: in.read(session_id, 0, size);
100: int l = in.readUint16();
101: if ((l & 0x01) == 0x01) { // cipher suites length must be an even number
102: fatalAlert(AlertProtocol.DECODE_ERROR,
103: "DECODE ERROR: incorrect ClientHello");
104: }
105: size = l >> 1;
106: cipher_suites = new CipherSuite[size];
107: for (int i = 0; i < size; i++) {
108: byte b0 = (byte) in.read();
109: byte b1 = (byte) in.read();
110: cipher_suites[i] = CipherSuite.getByCode(b0, b1);
111: }
112: size = in.read();
113: compression_methods = new byte[size];
114: in.read(compression_methods, 0, size);
115: this .length = 38 + session_id.length
116: + (cipher_suites.length << 1)
117: + compression_methods.length;
118: if (this .length > length) {
119: fatalAlert(AlertProtocol.DECODE_ERROR,
120: "DECODE ERROR: incorrect ClientHello");
121: }
122: // for forward compatibility, extra data is permitted;
123: // must be ignored
124: if (this .length < length) {
125: in.skip(length - this .length);
126: this .length = length;
127: }
128: }
129:
130: /**
131: * Parse V2ClientHello
132: * @param in
133: * @throws IOException
134: */
135: public ClientHello(HandshakeIODataStream in) throws IOException {
136: if (in.readUint8() != 1) {
137: fatalAlert(AlertProtocol.DECODE_ERROR,
138: "DECODE ERROR: incorrect V2ClientHello");
139: }
140: client_version = new byte[2];
141: client_version[0] = (byte) in.readUint8();
142: client_version[1] = (byte) in.readUint8();
143: int cipher_spec_length = in.readUint16();
144: if (in.readUint16() != 0) { // session_id_length
145: // as client already knows the protocol known to a server it should
146: // initiate the connection in that native protocol
147: fatalAlert(AlertProtocol.DECODE_ERROR,
148: "DECODE ERROR: incorrect V2ClientHello, cannot be used for resuming");
149: }
150: int challenge_length = in.readUint16();
151: if (challenge_length < 16) {
152: fatalAlert(AlertProtocol.DECODE_ERROR,
153: "DECODE ERROR: incorrect V2ClientHello, short challenge data");
154: }
155: session_id = new byte[0];
156: cipher_suites = new CipherSuite[cipher_spec_length / 3];
157: for (int i = 0; i < cipher_suites.length; i++) {
158: byte b0 = (byte) in.read();
159: byte b1 = (byte) in.read();
160: byte b2 = (byte) in.read();
161: cipher_suites[i] = CipherSuite.getByCode(b0, b1, b2);
162: }
163: compression_methods = new byte[] { 0 }; // CompressionMethod.null
164:
165: if (challenge_length < 32) {
166: Arrays.fill(random, 0, 32 - challenge_length, (byte) 0);
167: System.arraycopy(in.read(challenge_length), 0, random,
168: 32 - challenge_length, challenge_length);
169: } else if (challenge_length == 32) {
170: System.arraycopy(in.read(32), 0, random, 0, 32);
171: } else {
172: System.arraycopy(in.read(challenge_length),
173: challenge_length - 32, random, 0, 32);
174: }
175: if (in.available() > 0) {
176: fatalAlert(AlertProtocol.DECODE_ERROR,
177: "DECODE ERROR: incorrect V2ClientHello, extra data");
178: }
179: this .length = 38 + session_id.length
180: + (cipher_suites.length << 1)
181: + compression_methods.length;
182: }
183:
184: /**
185: * Sends message
186: * @param out
187: */
188: public void send(HandshakeIODataStream out) {
189: out.write(client_version);
190: out.write(random);
191: out.writeUint8(session_id.length);
192: out.write(session_id);
193: int size = cipher_suites.length << 1;
194: out.writeUint16(size);
195: for (int i = 0; i < cipher_suites.length; i++) {
196: out.write(cipher_suites[i].toBytes());
197: }
198: out.writeUint8(compression_methods.length);
199: for (int i = 0; i < compression_methods.length; i++) {
200: out.write(compression_methods[i]);
201: }
202: }
203:
204: /**
205: * Returns client random
206: * @return client random
207: */
208: public byte[] getRandom() {
209: return random;
210: }
211:
212: /**
213: * Returns message type
214: * @return
215: */
216: public int getType() {
217: return Handshake.CLIENT_HELLO;
218: }
219: }
|