1 /* 2 * Copyright (c) 2015, 2020, 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.Locale; 32 import javax.net.ssl.SSLProtocolException; 33 import static sun.security.ssl.SSLExtension.CH_EC_POINT_FORMATS; 34 import sun.security.ssl.SSLExtension.ExtensionConsumer; 35 import sun.security.ssl.SSLExtension.SSLExtensionSpec; 36 import sun.security.ssl.SSLHandshake.HandshakeMessage; 37 import sun.security.ssl.NamedGroup.NamedGroupSpec; 38 39 /** 40 * Pack of the "ec_point_formats" extensions [RFC 4492]. 41 */ 42 final class ECPointFormatsExtension { 43 static final HandshakeProducer chNetworkProducer = 44 new CHECPointFormatsProducer(); 45 static final ExtensionConsumer chOnLoadConsumer = 46 new CHECPointFormatsConsumer(); 47 48 static final ExtensionConsumer shOnLoadConsumer = 49 new SHECPointFormatsConsumer(); 50 51 static final SSLStringizer epfStringizer = 52 new ECPointFormatsStringizer(); 53 54 /** 55 * The "ec_point_formats" extension. 56 */ 57 static class ECPointFormatsSpec implements SSLExtensionSpec { 58 static final ECPointFormatsSpec DEFAULT = 59 new ECPointFormatsSpec(new byte[] {ECPointFormat.UNCOMPRESSED.id}); 60 61 final byte[] formats; 62 ECPointFormatsSpec(byte[] formats)63 ECPointFormatsSpec(byte[] formats) { 64 this.formats = formats; 65 } 66 ECPointFormatsSpec(HandshakeContext hc, ByteBuffer m)67 private ECPointFormatsSpec(HandshakeContext hc, 68 ByteBuffer m) throws IOException { 69 if (!m.hasRemaining()) { 70 throw hc.conContext.fatal(Alert.DECODE_ERROR, 71 new SSLProtocolException( 72 "Invalid ec_point_formats extension: " + 73 "insufficient data")); 74 } 75 76 this.formats = Record.getBytes8(m); 77 } 78 hasUncompressedFormat()79 private boolean hasUncompressedFormat() { 80 for (byte format : formats) { 81 if (format == ECPointFormat.UNCOMPRESSED.id) { 82 return true; 83 } 84 } 85 86 return false; 87 } 88 89 @Override toString()90 public String toString() { 91 MessageFormat messageFormat = new MessageFormat( 92 "\"formats\": '['{0}']'", Locale.ENGLISH); 93 if (formats == null || formats.length == 0) { 94 Object[] messageFields = { 95 "<no EC point format specified>" 96 }; 97 return messageFormat.format(messageFields); 98 } else { 99 StringBuilder builder = new StringBuilder(512); 100 boolean isFirst = true; 101 for (byte pf : formats) { 102 if (isFirst) { 103 isFirst = false; 104 } else { 105 builder.append(", "); 106 } 107 108 builder.append(ECPointFormat.nameOf(pf)); 109 } 110 111 Object[] messageFields = { 112 builder.toString() 113 }; 114 115 return messageFormat.format(messageFields); 116 } 117 } 118 } 119 120 private static final class ECPointFormatsStringizer implements SSLStringizer { 121 @Override toString(HandshakeContext hc, ByteBuffer buffer)122 public String toString(HandshakeContext hc, ByteBuffer buffer) { 123 try { 124 return (new ECPointFormatsSpec(hc, buffer)).toString(); 125 } catch (IOException ioe) { 126 // For debug logging only, so please swallow exceptions. 127 return ioe.getMessage(); 128 } 129 } 130 } 131 132 private static enum ECPointFormat { 133 UNCOMPRESSED ((byte)0, "uncompressed"), 134 ANSIX962_COMPRESSED_PRIME ((byte)1, "ansiX962_compressed_prime"), 135 FMT_ANSIX962_COMPRESSED_CHAR2 ((byte)2, "ansiX962_compressed_char2"); 136 137 final byte id; 138 final String name; 139 ECPointFormat(byte id, String name)140 private ECPointFormat(byte id, String name) { 141 this.id = id; 142 this.name = name; 143 } 144 nameOf(int id)145 static String nameOf(int id) { 146 for (ECPointFormat pf: ECPointFormat.values()) { 147 if (pf.id == id) { 148 return pf.name; 149 } 150 } 151 return "UNDEFINED-EC-POINT-FORMAT(" + id + ")"; 152 } 153 } 154 155 /** 156 * Network data producer of a "ec_point_formats" extension in 157 * the ClientHello handshake message. 158 */ 159 private static final 160 class CHECPointFormatsProducer implements HandshakeProducer { 161 // Prevent instantiation of this class. CHECPointFormatsProducer()162 private CHECPointFormatsProducer() { 163 // blank 164 } 165 166 @Override produce(ConnectionContext context, HandshakeMessage message)167 public byte[] produce(ConnectionContext context, 168 HandshakeMessage message) throws IOException { 169 // The producing happens in client side only. 170 ClientHandshakeContext chc = (ClientHandshakeContext)context; 171 172 // Is it a supported and enabled extension? 173 if (!chc.sslConfig.isAvailable(CH_EC_POINT_FORMATS)) { 174 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 175 SSLLogger.fine( 176 "Ignore unavailable ec_point_formats extension"); 177 } 178 return null; 179 } 180 181 // Produce the extension. 182 // 183 // produce the extension only if EC cipher suite is activated. 184 if (NamedGroupSpec.NAMED_GROUP_ECDHE.isSupported( 185 chc.activeCipherSuites)) { 186 // We are using uncompressed ECPointFormat only at present. 187 byte[] extData = new byte[] {0x01, 0x00}; 188 189 // Update the context. 190 chc.handshakeExtensions.put( 191 CH_EC_POINT_FORMATS, ECPointFormatsSpec.DEFAULT); 192 193 return extData; 194 } 195 196 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 197 SSLLogger.fine( 198 "Need no ec_point_formats extension"); 199 } 200 return null; 201 } 202 } 203 204 /** 205 * Network data consumer of a "ec_point_formats" extension in 206 * the ClientHello handshake message. 207 */ 208 private static final 209 class CHECPointFormatsConsumer implements ExtensionConsumer { 210 // Prevent instantiation of this class. CHECPointFormatsConsumer()211 private CHECPointFormatsConsumer() { 212 // blank 213 } 214 215 @Override consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)216 public void consume(ConnectionContext context, 217 HandshakeMessage message, ByteBuffer buffer) throws IOException { 218 219 // The consuming happens in server side only. 220 ServerHandshakeContext shc = (ServerHandshakeContext)context; 221 222 // Is it a supported and enabled extension? 223 if (!shc.sslConfig.isAvailable(CH_EC_POINT_FORMATS)) { 224 if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { 225 SSLLogger.fine( 226 "Ignore unavailable ec_point_formats extension"); 227 } 228 return; // ignore the extension 229 } 230 231 // Parse the extension. 232 ECPointFormatsSpec spec = new ECPointFormatsSpec(shc, buffer); 233 234 // per RFC 4492, uncompressed points must always be supported. 235 if (!spec.hasUncompressedFormat()) { 236 throw shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 237 "Invalid ec_point_formats extension data: " + 238 "peer does not support uncompressed points"); 239 } 240 241 // Update the context. 242 shc.handshakeExtensions.put(CH_EC_POINT_FORMATS, spec); 243 244 // No impact on session resumption, as only uncompressed points 245 // are supported at present. 246 } 247 } 248 249 /** 250 * Network data consumer of a "ec_point_formats" extension in 251 * the ServerHello handshake message. 252 */ 253 private static final 254 class SHECPointFormatsConsumer implements ExtensionConsumer { 255 // Prevent instantiation of this class. SHECPointFormatsConsumer()256 private SHECPointFormatsConsumer() { 257 // blank 258 } 259 260 @Override consume(ConnectionContext context, HandshakeMessage message, ByteBuffer buffer)261 public void consume(ConnectionContext context, 262 HandshakeMessage message, ByteBuffer buffer) throws IOException { 263 264 // The consuming happens in client side only. 265 ClientHandshakeContext chc = (ClientHandshakeContext)context; 266 267 // In response to "ec_point_formats" extension request only 268 ECPointFormatsSpec requestedSpec = (ECPointFormatsSpec) 269 chc.handshakeExtensions.get(CH_EC_POINT_FORMATS); 270 if (requestedSpec == null) { 271 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 272 "Unexpected ec_point_formats extension in ServerHello"); 273 } 274 275 // Parse the extension. 276 ECPointFormatsSpec spec = new ECPointFormatsSpec(chc, buffer); 277 278 // per RFC 4492, uncompressed points must always be supported. 279 if (!spec.hasUncompressedFormat()) { 280 throw chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, 281 "Invalid ec_point_formats extension data: " + 282 "peer does not support uncompressed points"); 283 } 284 285 // Update the context. 286 chc.handshakeExtensions.put(CH_EC_POINT_FORMATS, spec); 287 288 // No impact on session resumption, as only uncompressed points 289 // are supported at present. 290 } 291 } 292 } 293