--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/java.base/share/classes/sun/security/ssl/ECPointFormatsExtension.java Fri May 11 15:53:12 2018 -0700
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2015, 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
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.ssl;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.text.MessageFormat;
+import java.util.Locale;
+import javax.net.ssl.SSLProtocolException;
+import static sun.security.ssl.SSLExtension.CH_EC_POINT_FORMATS;
+import sun.security.ssl.SSLExtension.ExtensionConsumer;
+import sun.security.ssl.SSLExtension.SSLExtensionSpec;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+import sun.security.ssl.SupportedGroupsExtension.NamedGroupType;
+
+/**
+ * Pack of the "ec_point_formats" extensions [RFC 4492].
+ */
+final class ECPointFormatsExtension {
+ static final HandshakeProducer chNetworkProducer =
+ new CHECPointFormatsProducer();
+ static final ExtensionConsumer chOnLoadConcumer =
+ new CHECPointFormatsConsumer();
+
+ static final ExtensionConsumer shOnLoadConcumer =
+ new SHECPointFormatsConsumer();
+
+ static final SSLStringize epfStringize =
+ new ECPointFormatsStringize();
+
+ /**
+ * The "ec_point_formats" extension.
+ */
+ static class ECPointFormatsSpec implements SSLExtensionSpec {
+ static final ECPointFormatsSpec DEFAULT =
+ new ECPointFormatsSpec(new byte[] {ECPointFormat.UNCOMPRESSED.id});
+
+ final byte[] formats;
+
+ ECPointFormatsSpec(byte[] formats) {
+ this.formats = formats;
+ }
+
+ private ECPointFormatsSpec(ByteBuffer m) throws IOException {
+ if (!m.hasRemaining()) {
+ throw new SSLProtocolException(
+ "Invalid ec_point_formats extension: " +
+ "insufficient data");
+ }
+
+ this.formats = Record.getBytes8(m);
+ }
+
+ private boolean hasUncompressedFormat() {
+ for (byte format : formats) {
+ if (format == ECPointFormat.UNCOMPRESSED.id) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ MessageFormat messageFormat = new MessageFormat(
+ "\"formats\": '['{0}']'", Locale.ENGLISH);
+ if (formats == null || formats.length == 0) {
+ Object[] messageFields = {
+ "<no EC point format specified>"
+ };
+ return messageFormat.format(messageFields);
+ } else {
+ StringBuilder builder = new StringBuilder(512);
+ boolean isFirst = true;
+ for (byte pf : formats) {
+ if (isFirst) {
+ isFirst = false;
+ } else {
+ builder.append(", ");
+ }
+
+ builder.append(ECPointFormat.nameOf(pf));
+ }
+
+ Object[] messageFields = {
+ builder.toString()
+ };
+
+ return messageFormat.format(messageFields);
+ }
+ }
+ }
+
+ private static final class ECPointFormatsStringize implements SSLStringize {
+ @Override
+ public String toString(ByteBuffer buffer) {
+ try {
+ return (new ECPointFormatsSpec(buffer)).toString();
+ } catch (IOException ioe) {
+ // For debug logging only, so please swallow exceptions.
+ return ioe.getMessage();
+ }
+ }
+ }
+
+ private static enum ECPointFormat {
+ UNCOMPRESSED ((byte)0, "uncompressed"),
+ ANSIX962_COMPRESSED_PRIME ((byte)1, "ansiX962_compressed_prime"),
+ FMT_ANSIX962_COMPRESSED_CHAR2 ((byte)2, "ansiX962_compressed_char2");
+
+ final byte id;
+ final String name;
+
+ private ECPointFormat(byte id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ static String nameOf(int id) {
+ for (ECPointFormat pf: ECPointFormat.values()) {
+ if (pf.id == id) {
+ return pf.name;
+ }
+ }
+ return "UNDEFINED-EC-POINT-FORMAT(" + id + ")";
+ }
+ }
+
+ /**
+ * Network data producer of a "ec_point_formats" extension in
+ * the ClientHello handshake message.
+ */
+ private static final
+ class CHECPointFormatsProducer implements HandshakeProducer {
+ // Prevent instantiation of this class.
+ private CHECPointFormatsProducer() {
+ // blank
+ }
+
+ @Override
+ public byte[] produce(ConnectionContext context,
+ HandshakeMessage message) throws IOException {
+ // The producing happens in client side only.
+ ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+ // Is it a supported and enabled extension?
+ if (!chc.sslConfig.isAvailable(CH_EC_POINT_FORMATS)) {
+ if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+ SSLLogger.fine(
+ "Ignore unavailable ec_point_formats extension");
+ }
+ return null;
+ }
+
+ // Produce the extension.
+ //
+ // produce the extension only if EC cipher suite is activated.
+ if (NamedGroupType.NAMED_GROUP_ECDHE.isSupported(
+ chc.activeCipherSuites)) {
+ // We are using uncompressed ECPointFormat only at present.
+ byte[] extData = new byte[] {0x01, 0x00};
+
+ // Update the context.
+ chc.handshakeExtensions.put(
+ CH_EC_POINT_FORMATS, ECPointFormatsSpec.DEFAULT);
+
+ return extData;
+ }
+
+ if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+ SSLLogger.fine(
+ "Need no ec_point_formats extension");
+ }
+ return null;
+ }
+ }
+
+ /**
+ * Network data consumer of a "ec_point_formats" extension in
+ * the ClientHello handshake message.
+ */
+ private static final
+ class CHECPointFormatsConsumer implements ExtensionConsumer {
+ // Prevent instantiation of this class.
+ private CHECPointFormatsConsumer() {
+ // blank
+ }
+
+ @Override
+ public void consume(ConnectionContext context,
+ HandshakeMessage message, ByteBuffer buffer) throws IOException {
+
+ // The comsuming happens in server side only.
+ ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+ // Is it a supported and enabled extension?
+ if (!shc.sslConfig.isAvailable(CH_EC_POINT_FORMATS)) {
+ if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+ SSLLogger.fine(
+ "Ignore unavailable ec_point_formats extension");
+ }
+ return; // ignore the extension
+ }
+
+ // Parse the extension.
+ ECPointFormatsSpec spec;
+ try {
+ spec = new ECPointFormatsSpec(buffer);
+ } catch (IOException ioe) {
+ shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+ return; // fatal() always throws, make the compiler happy.
+ }
+
+ // per RFC 4492, uncompressed points must always be supported.
+ if (!spec.hasUncompressedFormat()) {
+ shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+ "Invalid ec_point_formats extension data: " +
+ "peer does not support uncompressed points");
+ }
+
+ // Update the context.
+ shc.handshakeExtensions.put(CH_EC_POINT_FORMATS, spec);
+
+ // No impact on session resumption, as only uncompressed points
+ // are supported at present.
+ }
+ }
+
+ /**
+ * Network data consumer of a "ec_point_formats" extension in
+ * the ServerHello handshake message.
+ */
+ private static final
+ class SHECPointFormatsConsumer implements ExtensionConsumer {
+ // Prevent instantiation of this class.
+ private SHECPointFormatsConsumer() {
+ // blank
+ }
+
+ @Override
+ public void consume(ConnectionContext context,
+ HandshakeMessage message, ByteBuffer buffer) throws IOException {
+
+ // The comsuming happens in client side only.
+ ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+ // In response to "ec_point_formats" extension request only
+ ECPointFormatsSpec requestedSpec = (ECPointFormatsSpec)
+ chc.handshakeExtensions.get(CH_EC_POINT_FORMATS);
+ if (requestedSpec == null) {
+ chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+ "Unexpected ec_point_formats extension in ServerHello");
+ }
+
+ // Parse the extension.
+ ECPointFormatsSpec spec;
+ try {
+ spec = new ECPointFormatsSpec(buffer);
+ } catch (IOException ioe) {
+ chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+ return; // fatal() always throws, make the compiler happy.
+ }
+
+ // per RFC 4492, uncompressed points must always be supported.
+ if (!spec.hasUncompressedFormat()) {
+ chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE,
+ "Invalid ec_point_formats extension data: " +
+ "peer does not support uncompressed points");
+ }
+
+ // Update the context.
+ chc.handshakeExtensions.put(CH_EC_POINT_FORMATS, spec);
+
+ // No impact on session resumption, as only uncompressed points
+ // are supported at present.
+ }
+ }
+}