1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package libcore.tlswire.handshake; 17 18 import java.io.ByteArrayInputStream; 19 import java.io.DataInput; 20 import java.io.DataInputStream; 21 import java.io.EOFException; 22 import java.io.IOException; 23 import java.math.BigInteger; 24 import java.util.ArrayList; 25 import java.util.List; 26 import libcore.tlswire.util.IoUtils; 27 import libcore.tlswire.util.TlsProtocolVersion; 28 29 /** 30 * {@link ClientHello} {@link HandshakeMessage} from TLS 1.2 RFC 5246. 31 */ 32 public class ClientHello extends HandshakeMessage { 33 public TlsProtocolVersion clientVersion; 34 public byte[] random; 35 public byte[] sessionId; 36 public List<CipherSuite> cipherSuites; 37 public List<CompressionMethod> compressionMethods; 38 /** Extensions or {@code null} for no extensions. */ 39 public List<HelloExtension> extensions; 40 @Override 41 protected void parseBody(DataInput in) throws IOException { 42 clientVersion = TlsProtocolVersion.read(in); 43 random = new byte[32]; 44 in.readFully(random); 45 sessionId = IoUtils.readTlsVariableLengthByteVector(in, 32); 46 int[] cipherSuiteCodes = IoUtils.readTlsVariableLengthUnsignedShortVector(in, 0xfffe); 47 cipherSuites = new ArrayList<CipherSuite>(cipherSuiteCodes.length); 48 for (int i = 0; i < cipherSuiteCodes.length; i++) { 49 cipherSuites.add(CipherSuite.valueOf(cipherSuiteCodes[i])); 50 } 51 byte[] compressionMethodCodes = IoUtils.readTlsVariableLengthByteVector(in, 0xff); 52 compressionMethods = new ArrayList<CompressionMethod>(compressionMethodCodes.length); 53 for (int i = 0; i < compressionMethodCodes.length; i++) { 54 int code = compressionMethodCodes[i] & 0xff; 55 compressionMethods.add(CompressionMethod.valueOf(code)); 56 } 57 int extensionsSectionSize; 58 try { 59 extensionsSectionSize = in.readUnsignedShort(); 60 } catch (EOFException e) { 61 // No extensions present 62 extensionsSectionSize = 0; 63 } 64 if (extensionsSectionSize > 0) { 65 extensions = new ArrayList<HelloExtension>(); 66 byte[] extensionsBytes = new byte[extensionsSectionSize]; 67 in.readFully(extensionsBytes); 68 ByteArrayInputStream extensionsIn = new ByteArrayInputStream(extensionsBytes); 69 DataInput extensionsDataIn = new DataInputStream(extensionsIn); 70 while (extensionsIn.available() > 0) { 71 try { 72 extensions.add(HelloExtension.read(extensionsDataIn)); 73 } catch (IOException e) { 74 throw new IOException( 75 "Failed to read HelloExtension #" + (extensions.size() + 1)); 76 } 77 } 78 } 79 } 80 public HelloExtension findExtensionByType(int extensionType) { 81 if (extensions == null) { 82 return null; 83 } 84 for (HelloExtension extension : extensions) { 85 if (extension.type == extensionType) { 86 return extension; 87 } 88 } 89 return null; 90 } 91 @Override 92 public String toString() { 93 return "ClientHello{client version: " + clientVersion + ", random: " 94 + new BigInteger(1, random).toString(16) + ", sessionId: " 95 + new BigInteger(1, sessionId).toString(16) + ", cipher suites: " + cipherSuites 96 + ", compression methods: " + compressionMethods 97 + ((extensions != null) ? (", extensions: " + String.valueOf(extensions)) : "") 98 + "}"; 99 } 100 } 101