< prev index next >
src/java.base/share/classes/sun/security/ssl/SSLExtensions.java
Print this page
*** 1,7 ****
/*
! * Copyright (c) 2006, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
--- 1,7 ----
/*
! * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
*** 24,163 ****
*/
package sun.security.ssl;
import java.io.IOException;
! import java.io.PrintStream;
import java.util.*;
! import javax.net.ssl.*;
/**
! * This file contains all the classes relevant to TLS Extensions for the
! * ClientHello and ServerHello messages. The extension mechanism and
! * several extensions are defined in RFC 6066. Additional extensions are
! * defined in the ECC RFC 4492 and the ALPN extension is defined in RFC 7301.
! *
! * Currently, only the two ECC extensions are fully supported.
! *
! * The classes contained in this file are:
! * . HelloExtensions: a List of extensions as used in the client hello
! * and server hello messages.
! * . ExtensionType: an enum style class for the extension type
! * . HelloExtension: abstract base class for all extensions. All subclasses
! * must be immutable.
! *
! * . UnknownExtension: used to represent all parsed extensions that we do not
! * explicitly support.
! * . ServerNameExtension: the server_name extension.
! * . SignatureAlgorithmsExtension: the signature_algorithms extension.
! * . SupportedGroupsExtension: the supported groups extension.
! * . EllipticPointFormatsExtension: the ECC supported point formats
! * (compressed/uncompressed) extension.
! * . ALPNExtension: the application_layer_protocol_negotiation extension.
! *
! * @since 1.6
! * @author Andreas Sterbenz
*/
! final class HelloExtensions {
!
! private List<HelloExtension> extensions;
private int encodedLength;
! HelloExtensions() {
! extensions = Collections.emptyList();
}
! HelloExtensions(HandshakeInStream s) throws IOException {
! int len = s.getInt16();
! extensions = new ArrayList<HelloExtension>();
! encodedLength = len + 2;
while (len > 0) {
! int type = s.getInt16();
! int extlen = s.getInt16();
! ExtensionType extType = ExtensionType.get(type);
! HelloExtension extension;
! if (extType == ExtensionType.EXT_SERVER_NAME) {
! extension = new ServerNameExtension(s, extlen);
! } else if (extType == ExtensionType.EXT_SIGNATURE_ALGORITHMS) {
! extension = new SignatureAlgorithmsExtension(s, extlen);
! } else if (extType == ExtensionType.EXT_SUPPORTED_GROUPS) {
! extension = new SupportedGroupsExtension(s, extlen);
! } else if (extType == ExtensionType.EXT_EC_POINT_FORMATS) {
! extension = new EllipticPointFormatsExtension(s, extlen);
! } else if (extType == ExtensionType.EXT_RENEGOTIATION_INFO) {
! extension = new RenegotiationInfoExtension(s, extlen);
! } else if (extType == ExtensionType.EXT_ALPN) {
! extension = new ALPNExtension(s, extlen);
! } else if (extType == ExtensionType.EXT_MAX_FRAGMENT_LENGTH) {
! extension = new MaxFragmentLengthExtension(s, extlen);
! } else if (extType == ExtensionType.EXT_STATUS_REQUEST) {
! extension = new CertStatusReqExtension(s, extlen);
! } else if (extType == ExtensionType.EXT_STATUS_REQUEST_V2) {
! extension = new CertStatusReqListV2Extension(s, extlen);
! } else if (extType == ExtensionType.EXT_EXTENDED_MASTER_SECRET) {
! extension = new ExtendedMasterSecretExtension(s, extlen);
! } else {
! extension = new UnknownExtension(s, extlen, extType);
! }
! extensions.add(extension);
! len -= extlen + 4;
! }
! if (len != 0) {
! throw new SSLProtocolException(
! "Error parsing extensions: extra data");
}
}
! // Return the List of extensions. Must not be modified by the caller.
! List<HelloExtension> list() {
! return extensions;
! }
!
! void add(HelloExtension ext) {
! if (extensions.isEmpty()) {
! extensions = new ArrayList<HelloExtension>();
! }
! extensions.add(ext);
! encodedLength = -1;
! }
!
! HelloExtension get(ExtensionType type) {
! for (HelloExtension ext : extensions) {
! if (ext.type == type) {
! return ext;
}
}
! return null;
}
! int length() {
! if (encodedLength >= 0) {
! return encodedLength;
}
! if (extensions.isEmpty()) {
! encodedLength = 0;
} else {
! encodedLength = 2;
! for (HelloExtension ext : extensions) {
! encodedLength += ext.length();
}
}
return encodedLength;
}
! void send(HandshakeOutStream s) throws IOException {
! int length = length();
! if (length == 0) {
return;
}
! s.putInt16(length - 2);
! for (HelloExtension ext : extensions) {
! ext.send(s);
}
}
! void print(PrintStream s) throws IOException {
! for (HelloExtension ext : extensions) {
! s.println(ext.toString());
}
}
}
--- 24,360 ----
*/
package sun.security.ssl;
import java.io.IOException;
! import java.nio.ByteBuffer;
! import java.text.MessageFormat;
import java.util.*;
!
! import sun.security.ssl.SSLHandshake.HandshakeMessage;
! import sun.security.util.HexDumpEncoder;
/**
! * SSL/(D)TLS extensions in a handshake message.
*/
! final class SSLExtensions {
! private final HandshakeMessage handshakeMessage;
! private Map<SSLExtension, byte[]> extMap = new LinkedHashMap<>();
private int encodedLength;
! // Extension map for debug logging
! private final Map<Integer, byte[]> logMap =
! SSLLogger.isOn ? null : new LinkedHashMap<>();
!
! SSLExtensions(HandshakeMessage handshakeMessage) {
! this.handshakeMessage = handshakeMessage;
! this.encodedLength = 2; // 2: the length of the extensions.
}
! SSLExtensions(HandshakeMessage hm,
! ByteBuffer m, SSLExtension[] extensions) throws IOException {
! this.handshakeMessage = hm;
!
! int len = Record.getInt16(m);
! encodedLength = len + 2; // 2: the length of the extensions.
while (len > 0) {
! int extId = Record.getInt16(m);
! int extLen = Record.getInt16(m);
! if (extLen > m.remaining()) {
! hm.handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER,
! "Error parsing extension (" + extId +
! "): no sufficient data");
! }
!
! SSLHandshake handshakeType = hm.handshakeType();
! if (SSLExtension.isConsumable(extId) &&
! SSLExtension.valueOf(handshakeType, extId) == null) {
! hm.handshakeContext.conContext.fatal(
! Alert.UNSUPPORTED_EXTENSION,
! "extension (" + extId +
! ") should not be presented in " + handshakeType.name);
! }
!
! boolean isSupported = false;
! for (SSLExtension extension : extensions) {
! if ((extension.id != extId) ||
! (extension.onLoadConcumer == null)) {
! continue;
! }
!
! if (extension.handshakeType != handshakeType) {
! hm.handshakeContext.conContext.fatal(
! Alert.UNSUPPORTED_EXTENSION,
! "extension (" + extId + ") should not be " +
! "presented in " + handshakeType.name);
! }
!
! byte[] extData = new byte[extLen];
! m.get(extData);
! extMap.put(extension, extData);
! if (logMap != null) {
! logMap.put(extId, extData);
! }
!
! isSupported = true;
! break;
! }
!
! if (!isSupported) {
! if (logMap != null) {
! // cache the extension for debug logging
! byte[] extData = new byte[extLen];
! m.get(extData);
! logMap.put(extId, extData);
!
! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
! SSLLogger.fine(
! "Ignore unknown or unsupported extension",
! toString(extId, extData));
! }
! } else {
! // ignore the extension
! int pos = m.position() + extLen;
! m.position(pos);
}
}
! len -= extLen + 4;
}
}
!
! byte[] get(SSLExtension ext) {
! return extMap.get(ext);
}
! /**
! * Consume the specified extensions.
! */
! void consumeOnLoad(HandshakeContext context,
! SSLExtension[] extensions) throws IOException {
! for (SSLExtension extension : extensions) {
! if (context.negotiatedProtocol != null &&
! !extension.isAvailable(context.negotiatedProtocol)) {
! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
! SSLLogger.fine(
! "Ignore unsupported extension: " + extension.name);
! }
! continue;
! // context.conContext.fatal(Alert.UNSUPPORTED_EXTENSION,
! // context.negotiatedProtocol + " does not support " +
! // extension + " extension");
! }
!
! if (!extMap.containsKey(extension)) {
! if (extension.onLoadAbsence != null) {
! extension.absent(context, handshakeMessage);
! } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
! SSLLogger.fine(
! "Ignore unavailable extension: " + extension.name);
! }
! continue;
! }
!
!
! if (extension.onLoadConcumer == null) {
! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
! SSLLogger.warning(
! "Ignore unsupported extension: " + extension.name);
! }
! continue;
! }
!
! ByteBuffer m = ByteBuffer.wrap(extMap.get(extension));
! extension.consumeOnLoad(context, handshakeMessage, m);
!
! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
! SSLLogger.fine("Consumed extension: " + extension.name);
! }
! }
! }
!
! /**
! * Consider impact of the specified extensions.
! */
! void consumeOnTrade(HandshakeContext context,
! SSLExtension[] extensions) throws IOException {
! for (SSLExtension extension : extensions) {
! if (!extMap.containsKey(extension)) {
! // No impact could be expected, so just ignore the absence.
! continue;
! }
!
! if (extension.onTradeConsumer == null) {
! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
! SSLLogger.warning(
! "Ignore impact of unsupported extension: " +
! extension.name);
! }
! continue;
! }
!
! extension.consumeOnTrade(context, handshakeMessage);
! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
! SSLLogger.fine("Populated with extension: " + extension.name);
! }
! }
}
!
! /**
! * Produce extension values for the specified extensions.
! */
! void produce(HandshakeContext context,
! SSLExtension[] extensions) throws IOException {
! for (SSLExtension extension : extensions) {
! if (extMap.containsKey(extension)) {
! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
! SSLLogger.fine(
! "Ignore, duplicated extension: " +
! extension.name);
! }
! continue;
! }
!
! if (extension.networkProducer == null) {
! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
! SSLLogger.warning(
! "Ignore, no extension producer defined: " +
! extension.name);
! }
! continue;
! }
!
! byte[] encoded = extension.produce(context, handshakeMessage);
! if (encoded != null) {
! extMap.put(extension, encoded);
! encodedLength += encoded.length + 4; // extension_type (2)
! // extension_data length(2)
! } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
! // The extension is not available in the context.
! SSLLogger.fine(
! "Ignore, context unavailable extension: " +
! extension.name);
! }
! }
! }
!
! /**
! * Produce extension values for the specified extensions, replacing if
! * there is an existing extension value for a specified extension.
! */
! void reproduce(HandshakeContext context,
! SSLExtension[] extensions) throws IOException {
! for (SSLExtension extension : extensions) {
! if (extension.networkProducer == null) {
! if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
! SSLLogger.warning(
! "Ignore, no extension producer defined: " +
! extension.name);
! }
! continue;
! }
!
! byte[] encoded = extension.produce(context, handshakeMessage);
! if (encoded != null) {
! if (extMap.containsKey(extension)) {
! byte[] old = extMap.replace(extension, encoded);
! if (old != null) {
! encodedLength -= old.length + 4;
! }
! encodedLength += encoded.length + 4;
} else {
! extMap.put(extension, encoded);
! encodedLength += encoded.length + 4;
! // extension_type (2)
! // extension_data length(2)
}
+ } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+ // The extension is not available in the context.
+ SSLLogger.fine(
+ "Ignore, context unavailable extension: " +
+ extension.name);
}
+ }
+ }
+
+ // Note that TLS 1.3 may use empty extensions. Please consider it while
+ // using this method.
+ int length() {
+ if (extMap.isEmpty()) {
+ return 0;
+ } else {
return encodedLength;
}
+ }
! // Note that TLS 1.3 may use empty extensions. Please consider it while
! // using this method.
! void send(HandshakeOutStream hos) throws IOException {
! int extsLen = length();
! if (extsLen == 0) {
return;
}
! hos.putInt16(extsLen - 2);
! // extensions must be sent in the order they appear in the enum
! for (SSLExtension ext : SSLExtension.values()) {
! byte[] extData = extMap.get(ext);
! if (extData != null) {
! hos.putInt16(ext.id);
! hos.putBytes16(extData);
! }
! }
! }
!
! @Override
! public String toString() {
! if (extMap.isEmpty() && (logMap == null || logMap.isEmpty())) {
! return "<no extension>";
! } else {
! StringBuilder builder = new StringBuilder(512);
! if (logMap != null) {
! for (Map.Entry<Integer, byte[]> en : logMap.entrySet()) {
! SSLExtension ext = SSLExtension.valueOf(
! handshakeMessage.handshakeType(), en.getKey());
! if (builder.length() != 0) {
! builder.append(",\n");
! }
! if (ext != null) {
! builder.append(
! ext.toString(ByteBuffer.wrap(en.getValue())));
! } else {
! builder.append(toString(en.getKey(), en.getValue()));
! }
}
+
+ return builder.toString();
+ } else {
+ for (Map.Entry<SSLExtension, byte[]> en : extMap.entrySet()) {
+ if (builder.length() != 0) {
+ builder.append(",\n");
+ }
+ builder.append(
+ en.getKey().toString(ByteBuffer.wrap(en.getValue())));
}
! return builder.toString();
! }
! }
}
+
+ private static String toString(int extId, byte[] extData) {
+ MessageFormat messageFormat = new MessageFormat(
+ "\"unknown extension ({0})\": '{'\n" +
+ "{1}\n" +
+ "'}'",
+ Locale.ENGLISH);
+
+ HexDumpEncoder hexEncoder = new HexDumpEncoder();
+ String encoded = hexEncoder.encodeBuffer(extData);
+
+ Object[] messageFields = {
+ extId,
+ Utilities.indent(encoded)
+ };
+
+ return messageFormat.format(messageFields);
}
}
< prev index next >