1 /* 2 * Copyright (c) 2006, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.ssl; 27 28 import java.io.IOException; 29 import java.io.PrintStream; 30 import java.util.*; 31 import javax.net.ssl.*; 32 33 /** 34 * This file contains all the classes relevant to TLS Extensions for the 35 * ClientHello and ServerHello messages. The extension mechanism and 36 * several extensions are defined in RFC 6066. Additional extensions are 37 * defined in the ECC RFC 4492 and the ALPN extension is defined in RFC 7301. 38 * 39 * Currently, only the two ECC extensions are fully supported. 40 * 41 * The classes contained in this file are: 42 * . HelloExtensions: a List of extensions as used in the client hello 43 * and server hello messages. 44 * . ExtensionType: an enum style class for the extension type 45 * . HelloExtension: abstract base class for all extensions. All subclasses 46 * must be immutable. 47 * 48 * . UnknownExtension: used to represent all parsed extensions that we do not 49 * explicitly support. 50 * . ServerNameExtension: the server_name extension. 51 * . SignatureAlgorithmsExtension: the signature_algorithms extension. 52 * . SupportedGroupsExtension: the supported groups extension. 53 * . EllipticPointFormatsExtension: the ECC supported point formats 54 * (compressed/uncompressed) extension. 55 * . ALPNExtension: the application_layer_protocol_negotiation extension. 56 * 57 * @since 1.6 58 * @author Andreas Sterbenz 59 */ 60 final class HelloExtensions { 61 62 private List<HelloExtension> extensions; 63 private int encodedLength; 64 65 HelloExtensions() { 66 extensions = Collections.emptyList(); 67 } 68 69 HelloExtensions(HandshakeInStream s) throws IOException { 70 int len = s.getInt16(); 71 extensions = new ArrayList<HelloExtension>(); 72 encodedLength = len + 2; 73 while (len > 0) { 74 int type = s.getInt16(); 75 int extlen = s.getInt16(); 76 ExtensionType extType = ExtensionType.get(type); 77 HelloExtension extension; 78 if (extType == ExtensionType.EXT_SERVER_NAME) { 79 extension = new ServerNameExtension(s, extlen); 80 } else if (extType == ExtensionType.EXT_SIGNATURE_ALGORITHMS) { 81 extension = new SignatureAlgorithmsExtension(s, extlen); 82 } else if (extType == ExtensionType.EXT_SUPPORTED_GROUPS) { 83 extension = new SupportedGroupsExtension(s, extlen); 84 } else if (extType == ExtensionType.EXT_EC_POINT_FORMATS) { 85 extension = new EllipticPointFormatsExtension(s, extlen); 86 } else if (extType == ExtensionType.EXT_RENEGOTIATION_INFO) { 87 extension = new RenegotiationInfoExtension(s, extlen); 88 } else if (extType == ExtensionType.EXT_ALPN) { 89 extension = new ALPNExtension(s, extlen); 90 } else if (extType == ExtensionType.EXT_MAX_FRAGMENT_LENGTH) { 91 extension = new MaxFragmentLengthExtension(s, extlen); 92 } else if (extType == ExtensionType.EXT_STATUS_REQUEST) { 93 extension = new CertStatusReqExtension(s, extlen); 94 } else if (extType == ExtensionType.EXT_STATUS_REQUEST_V2) { 95 extension = new CertStatusReqListV2Extension(s, extlen); 96 } else if (extType == ExtensionType.EXT_EXTENDED_MASTER_SECRET) { 97 extension = new ExtendedMasterSecretExtension(s, extlen); 98 } else { 99 extension = new UnknownExtension(s, extlen, extType); 100 } 101 extensions.add(extension); 102 len -= extlen + 4; 103 } 104 if (len != 0) { 105 throw new SSLProtocolException( 106 "Error parsing extensions: extra data"); 107 } 108 } 109 110 // Return the List of extensions. Must not be modified by the caller. 111 List<HelloExtension> list() { 112 return extensions; 113 } 114 115 void add(HelloExtension ext) { 116 if (extensions.isEmpty()) { 117 extensions = new ArrayList<HelloExtension>(); 118 } 119 extensions.add(ext); 120 encodedLength = -1; 121 } 122 123 HelloExtension get(ExtensionType type) { 124 for (HelloExtension ext : extensions) { 125 if (ext.type == type) { 126 return ext; 127 } 128 } 129 return null; 130 } 131 132 int length() { 133 if (encodedLength >= 0) { 134 return encodedLength; 135 } 136 if (extensions.isEmpty()) { 137 encodedLength = 0; 138 } else { 139 encodedLength = 2; 140 for (HelloExtension ext : extensions) { 141 encodedLength += ext.length(); 142 } 143 } 144 return encodedLength; 145 } 146 147 void send(HandshakeOutStream s) throws IOException { 148 int length = length(); 149 if (length == 0) { 150 return; 151 } 152 s.putInt16(length - 2); 153 for (HelloExtension ext : extensions) { 154 ext.send(s); 155 } 156 } 157 158 void print(PrintStream s) throws IOException { 159 for (HelloExtension ext : extensions) { 160 s.println(ext.toString()); 161 } 162 } 163 } | 1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.ssl; 27 28 import java.io.IOException; 29 import java.nio.ByteBuffer; 30 import java.text.MessageFormat; 31 import java.util.*; 32 33 import sun.security.ssl.SSLHandshake.HandshakeMessage; 34 import sun.security.util.HexDumpEncoder; 35 36 /** 37 * SSL/(D)TLS extensions in a handshake message. 38 */ 39 final class SSLExtensions { 40 private final HandshakeMessage handshakeMessage; 41 private Map<SSLExtension, byte[]> extMap = new LinkedHashMap<>(); 42 private int encodedLength; 43 44 // Extension map for debug logging 45 private final Map<Integer, byte[]> logMap = 46 SSLLogger.isOn ? null : new LinkedHashMap<>(); 47 48 SSLExtensions(HandshakeMessage handshakeMessage) { 49 this.handshakeMessage = handshakeMessage; 50 this.encodedLength = 2; // 2: the length of the extensions. 51 } 52 53 SSLExtensions(HandshakeMessage hm, 54 ByteBuffer m, SSLExtension[] extensions) throws IOException { 55 this.handshakeMessage = hm; 56 57 int len = Record.getInt16(m); 58 encodedLength = len + 2; // 2: the length of the extensions. 59 while (len > 0) { 60 int extId = Record.getInt16(m); 61 int extLen = Record.getInt16(m); 62 if (extLen > m.remaining()) { 63 hm.handshakeContext.conContext.fatal(Alert.ILLEGAL_PARAMETER, 64 "Error parsing extension (" + extId + 65 "): no sufficient data"); 66 } 67 68 SSLHandshake handshakeType = hm.handshakeType(); 69 if (SSLExtension.isConsumable(extId) && 70 SSLExtension.valueOf(handshakeType, extId) == null) { 71 hm.handshakeContext.conContext.fatal( 72 Alert.UNSUPPORTED_EXTENSION, 73 "extension (" + extId + 74 ") should not be presented in " + handshakeType.name); 75 } 76 77 boolean isSupported = false; 78 for (SSLExtension extension : extensions) { 79 if ((extension.id != extId) || 80 (extension.onLoadConcumer == null)) { 81 continue; 82 } 83 84 if (extension.handshakeType != handshakeType) { 85 hm.handshakeContext.conContext.fatal( 86 Alert.UNSUPPORTED_EXTENSION, 87 "extension (" + extId + ") should not be " + 88 "presented in " + handshakeType.name); 89 } 90 91 byte[] extData = new byte[extLen]; 92 m.get(extData); 93 extMap.put(extension, extData); 94 if (logMap != null) { 95 logMap.put(extId, extData); 96 } 97 98 isSupported = true; 99 break; 100 } 101 102 if (!isSupported) { 103 if (logMap != null) { 104 // cache the extension for debug logging 105 byte[] extData = new byte[extLen]; 106 m.get(extData); 107 logMap.put(extId, extData); 108 109 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 110 SSLLogger.fine( 111 "Ignore unknown or unsupported extension", 112 toString(extId, extData)); 113 } 114 } else { 115 // ignore the extension 116 int pos = m.position() + extLen; 117 m.position(pos); 118 } 119 } 120 121 len -= extLen + 4; 122 } 123 } 124 125 byte[] get(SSLExtension ext) { 126 return extMap.get(ext); 127 } 128 129 /** 130 * Consume the specified extensions. 131 */ 132 void consumeOnLoad(HandshakeContext context, 133 SSLExtension[] extensions) throws IOException { 134 for (SSLExtension extension : extensions) { 135 if (context.negotiatedProtocol != null && 136 !extension.isAvailable(context.negotiatedProtocol)) { 137 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 138 SSLLogger.fine( 139 "Ignore unsupported extension: " + extension.name); 140 } 141 continue; 142 // context.conContext.fatal(Alert.UNSUPPORTED_EXTENSION, 143 // context.negotiatedProtocol + " does not support " + 144 // extension + " extension"); 145 } 146 147 if (!extMap.containsKey(extension)) { 148 if (extension.onLoadAbsence != null) { 149 extension.absent(context, handshakeMessage); 150 } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 151 SSLLogger.fine( 152 "Ignore unavailable extension: " + extension.name); 153 } 154 continue; 155 } 156 157 158 if (extension.onLoadConcumer == null) { 159 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 160 SSLLogger.warning( 161 "Ignore unsupported extension: " + extension.name); 162 } 163 continue; 164 } 165 166 ByteBuffer m = ByteBuffer.wrap(extMap.get(extension)); 167 extension.consumeOnLoad(context, handshakeMessage, m); 168 169 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 170 SSLLogger.fine("Consumed extension: " + extension.name); 171 } 172 } 173 } 174 175 /** 176 * Consider impact of the specified extensions. 177 */ 178 void consumeOnTrade(HandshakeContext context, 179 SSLExtension[] extensions) throws IOException { 180 for (SSLExtension extension : extensions) { 181 if (!extMap.containsKey(extension)) { 182 // No impact could be expected, so just ignore the absence. 183 continue; 184 } 185 186 if (extension.onTradeConsumer == null) { 187 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 188 SSLLogger.warning( 189 "Ignore impact of unsupported extension: " + 190 extension.name); 191 } 192 continue; 193 } 194 195 extension.consumeOnTrade(context, handshakeMessage); 196 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 197 SSLLogger.fine("Populated with extension: " + extension.name); 198 } 199 } 200 } 201 202 /** 203 * Produce extension values for the specified extensions. 204 */ 205 void produce(HandshakeContext context, 206 SSLExtension[] extensions) throws IOException { 207 for (SSLExtension extension : extensions) { 208 if (extMap.containsKey(extension)) { 209 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 210 SSLLogger.fine( 211 "Ignore, duplicated extension: " + 212 extension.name); 213 } 214 continue; 215 } 216 217 if (extension.networkProducer == null) { 218 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 219 SSLLogger.warning( 220 "Ignore, no extension producer defined: " + 221 extension.name); 222 } 223 continue; 224 } 225 226 byte[] encoded = extension.produce(context, handshakeMessage); 227 if (encoded != null) { 228 extMap.put(extension, encoded); 229 encodedLength += encoded.length + 4; // extension_type (2) 230 // extension_data length(2) 231 } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 232 // The extension is not available in the context. 233 SSLLogger.fine( 234 "Ignore, context unavailable extension: " + 235 extension.name); 236 } 237 } 238 } 239 240 /** 241 * Produce extension values for the specified extensions, replacing if 242 * there is an existing extension value for a specified extension. 243 */ 244 void reproduce(HandshakeContext context, 245 SSLExtension[] extensions) throws IOException { 246 for (SSLExtension extension : extensions) { 247 if (extension.networkProducer == null) { 248 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 249 SSLLogger.warning( 250 "Ignore, no extension producer defined: " + 251 extension.name); 252 } 253 continue; 254 } 255 256 byte[] encoded = extension.produce(context, handshakeMessage); 257 if (encoded != null) { 258 if (extMap.containsKey(extension)) { 259 byte[] old = extMap.replace(extension, encoded); 260 if (old != null) { 261 encodedLength -= old.length + 4; 262 } 263 encodedLength += encoded.length + 4; 264 } else { 265 extMap.put(extension, encoded); 266 encodedLength += encoded.length + 4; 267 // extension_type (2) 268 // extension_data length(2) 269 } 270 } else if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 271 // The extension is not available in the context. 272 SSLLogger.fine( 273 "Ignore, context unavailable extension: " + 274 extension.name); 275 } 276 } 277 } 278 279 // Note that TLS 1.3 may use empty extensions. Please consider it while 280 // using this method. 281 int length() { 282 if (extMap.isEmpty()) { 283 return 0; 284 } else { 285 return encodedLength; 286 } 287 } 288 289 // Note that TLS 1.3 may use empty extensions. Please consider it while 290 // using this method. 291 void send(HandshakeOutStream hos) throws IOException { 292 int extsLen = length(); 293 if (extsLen == 0) { 294 return; 295 } 296 hos.putInt16(extsLen - 2); 297 // extensions must be sent in the order they appear in the enum 298 for (SSLExtension ext : SSLExtension.values()) { 299 byte[] extData = extMap.get(ext); 300 if (extData != null) { 301 hos.putInt16(ext.id); 302 hos.putBytes16(extData); 303 } 304 } 305 } 306 307 @Override 308 public String toString() { 309 if (extMap.isEmpty() && (logMap == null || logMap.isEmpty())) { 310 return "<no extension>"; 311 } else { 312 StringBuilder builder = new StringBuilder(512); 313 if (logMap != null) { 314 for (Map.Entry<Integer, byte[]> en : logMap.entrySet()) { 315 SSLExtension ext = SSLExtension.valueOf( 316 handshakeMessage.handshakeType(), en.getKey()); 317 if (builder.length() != 0) { 318 builder.append(",\n"); 319 } 320 if (ext != null) { 321 builder.append( 322 ext.toString(ByteBuffer.wrap(en.getValue()))); 323 } else { 324 builder.append(toString(en.getKey(), en.getValue())); 325 } 326 } 327 328 return builder.toString(); 329 } else { 330 for (Map.Entry<SSLExtension, byte[]> en : extMap.entrySet()) { 331 if (builder.length() != 0) { 332 builder.append(",\n"); 333 } 334 builder.append( 335 en.getKey().toString(ByteBuffer.wrap(en.getValue()))); 336 } 337 338 return builder.toString(); 339 } 340 } 341 } 342 343 private static String toString(int extId, byte[] extData) { 344 MessageFormat messageFormat = new MessageFormat( 345 "\"unknown extension ({0})\": '{'\n" + 346 "{1}\n" + 347 "'}'", 348 Locale.ENGLISH); 349 350 HexDumpEncoder hexEncoder = new HexDumpEncoder(); 351 String encoded = hexEncoder.encodeBuffer(extData); 352 353 Object[] messageFields = { 354 extId, 355 Utilities.indent(encoded) 356 }; 357 358 return messageFormat.format(messageFields); 359 } 360 } |