src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java
branchJDK-8145252-TLS13-branch
changeset 56542 56aaa6cb3693
parent 47216 71c04702a3d5
child 56584 a0f3377c58c7
--- a/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java	Fri May 11 14:55:56 2018 -0700
+++ b/src/java.base/share/classes/sun/security/ssl/SignatureAlgorithmsExtension.java	Fri May 11 15:53:12 2018 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -26,110 +26,484 @@
 package sun.security.ssl;
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
+import java.nio.ByteBuffer;
+import java.text.MessageFormat;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import javax.net.ssl.SSLProtocolException;
+import sun.security.ssl.SSLExtension.ExtensionConsumer;
+import sun.security.ssl.SSLExtension.SSLExtensionSpec;
+import sun.security.ssl.SSLHandshake.HandshakeMessage;
+
+/**
+ * Pack of the "signature_algorithms" extensions [RFC 5246].
+ */
+final class SignatureAlgorithmsExtension {
+    static final HandshakeProducer chNetworkProducer =
+            new CHSignatureSchemesProducer();
+    static final ExtensionConsumer chOnLoadConcumer =
+            new CHSignatureSchemesConsumer();
+    static final HandshakeAbsence chOnLoadAbsence =
+            new CHSignatureSchemesAbsence();
+    static final HandshakeConsumer chOnTradeConsumer =
+            new CHSignatureSchemesUpdate();
 
-import javax.net.ssl.SSLProtocolException;
+    static final HandshakeProducer crNetworkProducer =
+            new CRSignatureSchemesProducer();
+    static final ExtensionConsumer crOnLoadConcumer =
+            new CRSignatureSchemesConsumer();
+    static final HandshakeAbsence crOnLoadAbsence =
+            new CRSignatureSchemesAbsence();
+    static final HandshakeConsumer crOnTradeConsumer =
+            new CRSignatureSchemesUpdate();
+
+    static final SSLStringize ssStringize =
+            new SignatureSchemesStringize();
+
+    /**
+     * The "signature_algorithms" extension.
+     */
+    static final class SignatureSchemesSpec implements SSLExtensionSpec {
+        final int[] signatureSchemes;
+
+        SignatureSchemesSpec(List<SignatureScheme> schemes) {
+            if (schemes != null) {
+                signatureSchemes = new int[schemes.size()];
+                int i = 0;
+                for (SignatureScheme scheme : schemes) {
+                    signatureSchemes[i++] = scheme.id;
+                }
+            } else {
+                this.signatureSchemes = new int[0];
+            }
+        }
 
-/*
- * [RFC5246] The client uses the "signature_algorithms" extension to
- * indicate to the server which signature/hash algorithm pairs may be
- * used in digital signatures.  The "extension_data" field of this
- * extension contains a "supported_signature_algorithms" value.
- *
- *     enum {
- *         none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5),
- *         sha512(6), (255)
- *     } HashAlgorithm;
- *
- *     enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) }
- *       SignatureAlgorithm;
- *
- *     struct {
- *           HashAlgorithm hash;
- *           SignatureAlgorithm signature;
- *     } SignatureAndHashAlgorithm;
- *
- *     SignatureAndHashAlgorithm
- *       supported_signature_algorithms<2..2^16-2>;
- */
-final class SignatureAlgorithmsExtension extends HelloExtension {
+        SignatureSchemesSpec(ByteBuffer buffer) throws IOException {
+            if (buffer.remaining() < 2) {      // 2: the length of the list
+                throw new SSLProtocolException(
+                    "Invalid signature_algorithms: insufficient data");
+            }
+
+            byte[] algs = Record.getBytes16(buffer);
+            if (buffer.hasRemaining()) {
+                throw new SSLProtocolException(
+                    "Invalid signature_algorithms: unknown extra data");
+            }
+
+            if (algs == null || algs.length == 0 || (algs.length & 0x01) != 0) {
+                throw new SSLProtocolException(
+                    "Invalid signature_algorithms: incomplete data");
+            }
+
+            int[] schemes = new int[algs.length / 2];
+            for (int i = 0, j = 0; i < algs.length;) {
+                byte hash = algs[i++];
+                byte sign = algs[i++];
+                schemes[j++] = ((hash & 0xFF) << 8) | (sign & 0xFF);
+            }
+
+            this.signatureSchemes = schemes;
+        }
 
-    private Collection<SignatureAndHashAlgorithm> algorithms;
-    private int algorithmsLen;  // length of supported_signature_algorithms
-
-    SignatureAlgorithmsExtension(
-            Collection<SignatureAndHashAlgorithm> signAlgs) {
+        @Override
+        public String toString() {
+            MessageFormat messageFormat = new MessageFormat(
+                "\"signature schemes\": '['{0}']'", Locale.ENGLISH);
 
-        super(ExtensionType.EXT_SIGNATURE_ALGORITHMS);
+            if (signatureSchemes == null || signatureSchemes.length == 0) {
+                Object[] messageFields = {
+                        "<no supported signature schemes specified>"
+                    };
+                return messageFormat.format(messageFields);
+            } else {
+                StringBuilder builder = new StringBuilder(512);
+                boolean isFirst = true;
+                for (int pv : signatureSchemes) {
+                    if (isFirst) {
+                        isFirst = false;
+                    } else {
+                        builder.append(", ");
+                    }
 
-        algorithms = new ArrayList<SignatureAndHashAlgorithm>(signAlgs);
-        algorithmsLen =
-            SignatureAndHashAlgorithm.sizeInRecord() * algorithms.size();
+                    builder.append(SignatureScheme.nameOf(pv));
+                }
+
+                Object[] messageFields = {
+                        builder.toString()
+                    };
+
+                return messageFormat.format(messageFields);
+            }
+        }
     }
 
-    SignatureAlgorithmsExtension(HandshakeInStream s, int len)
-                throws IOException {
-        super(ExtensionType.EXT_SIGNATURE_ALGORITHMS);
+    private static final
+            class SignatureSchemesStringize implements SSLStringize {
+        @Override
+        public String toString(ByteBuffer buffer) {
+            try {
+                return (new SignatureSchemesSpec(buffer)).toString();
+            } catch (IOException ioe) {
+                // For debug logging only, so please swallow exceptions.
+                return ioe.getMessage();
+            }
+        }
+    }
 
-        algorithmsLen = s.getInt16();
-        if (algorithmsLen == 0 || algorithmsLen + 2 != len) {
-            throw new SSLProtocolException("Invalid " + type + " extension");
+    /**
+     * Network data producer of a "signature_algorithms" extension in
+     * the ClientHello handshake message.
+     */
+    private static final
+            class CHSignatureSchemesProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CHSignatureSchemesProducer() {
+            // blank
         }
 
-        algorithms = new ArrayList<SignatureAndHashAlgorithm>();
-        int remains = algorithmsLen;
-        int sequence = 0;
-        while (remains > 1) {   // needs at least two bytes
-            int hash = s.getInt8();         // hash algorithm
-            int signature = s.getInt8();    // signature algorithm
+        @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(
+                    SSLExtension.CH_SIGNATURE_ALGORITHMS)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable signature_algorithms extension");
+                }
+                return null;
+            }
+
+            // Produce the extension.
+            if (chc.localSupportedSignAlgs == null) {
+                chc.localSupportedSignAlgs =
+                    SignatureScheme.getSupportedAlgorithms(
+                            chc.algorithmConstraints, chc.activeProtocols);
+            }
 
-            SignatureAndHashAlgorithm algorithm =
-                SignatureAndHashAlgorithm.valueOf(hash, signature, ++sequence);
-            algorithms.add(algorithm);
-            remains -= 2;  // one byte for hash, one byte for signature
+            int vectorLen = SignatureScheme.sizeInRecord() *
+                    chc.localSupportedSignAlgs.size();
+            byte[] extData = new byte[vectorLen + 2];
+            ByteBuffer m = ByteBuffer.wrap(extData);
+            Record.putInt16(m, vectorLen);
+            for (SignatureScheme ss : chc.localSupportedSignAlgs) {
+                Record.putInt16(m, ss.id);
+            }
+
+            // Update the context.
+            chc.handshakeExtensions.put(
+                    SSLExtension.CH_SIGNATURE_ALGORITHMS,
+                    new SignatureSchemesSpec(chc.localSupportedSignAlgs));
+
+            return extData;
+        }
+    }
+
+    /**
+     * Network data consumer of a "signature_algorithms" extension in
+     * the ClientHello handshake message.
+     */
+    private static final
+            class CHSignatureSchemesConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CHSignatureSchemesConsumer() {
+            // blank
         }
 
-        if (remains != 0) {
-            throw new SSLProtocolException("Invalid server_name extension");
+        @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(
+                    SSLExtension.CH_SIGNATURE_ALGORITHMS)) {
+                if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
+                    SSLLogger.fine(
+                        "Ignore unavailable signature_algorithms extension");
+                }
+                return;     // ignore the extension
+            }
+
+            // Parse the extension.
+            SignatureSchemesSpec spec;
+            try {
+                spec = new SignatureSchemesSpec(buffer);
+            } catch (IOException ioe) {
+                shc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            // Update the context.
+            shc.handshakeExtensions.put(
+                    SSLExtension.CH_SIGNATURE_ALGORITHMS, spec);
+
+            // No impact on session resumption.
         }
     }
 
-    Collection<SignatureAndHashAlgorithm> getSignAlgorithms() {
-        return algorithms;
-    }
+    /**
+     * After session creation consuming of a "signature_algorithms"
+     * extension in the ClientHello handshake message.
+     */
+    private static final class CHSignatureSchemesUpdate
+            implements HandshakeConsumer {
+        // Prevent instantiation of this class.
+        private CHSignatureSchemesUpdate() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The comsuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            SignatureSchemesSpec spec =
+                    (SignatureSchemesSpec)shc.handshakeExtensions.get(
+                            SSLExtension.CH_SIGNATURE_ALGORITHMS);
+            if (spec == null) {
+                // Ignore, no "signature_algorithms" extension requested.
+                return;
+            }
 
-    @Override
-    int length() {
-        return 6 + algorithmsLen;
+            // update the context
+            List<SignatureScheme> shemes =
+                    SignatureScheme.getSupportedAlgorithms(
+                            shc.algorithmConstraints, shc.negotiatedProtocol,
+                            spec.signatureSchemes);
+            shc.peerRequestedSignatureSchemes = shemes;
+
+            // If no "signature_algorithms_cert" extension is present, then
+            // the "signature_algorithms" extension also applies to
+            // signatures appearing in certificates.
+            SignatureSchemesSpec certSpec =
+                    (SignatureSchemesSpec)shc.handshakeExtensions.get(
+                            SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT);
+            if (certSpec == null) {
+                shc.peerRequestedCertSignSchemes = shemes;
+            }
+
+            shc.handshakeSession.setPeerSupportedSignatureAlgorithms(shemes);
+
+            if (!shc.isResumption && shc.negotiatedProtocol.useTLS13PlusSpec()) {
+                if (shc.sslConfig.clientAuthType !=
+                        ClientAuthType.CLIENT_AUTH_NONE) {
+                    shc.handshakeProducers.putIfAbsent(
+                            SSLHandshake.CERTIFICATE_REQUEST.id,
+                            SSLHandshake.CERTIFICATE_REQUEST);
+                }
+                shc.handshakeProducers.put(
+                        SSLHandshake.CERTIFICATE.id,
+                        SSLHandshake.CERTIFICATE);
+                shc.handshakeProducers.putIfAbsent(
+                        SSLHandshake.CERTIFICATE_VERIFY.id,
+                        SSLHandshake.CERTIFICATE_VERIFY);
+            }
+        }
     }
 
-    @Override
-    void send(HandshakeOutStream s) throws IOException {
-        s.putInt16(type.id);
-        s.putInt16(algorithmsLen + 2);
-        s.putInt16(algorithmsLen);
+    /**
+     * The absence processing if a "signature_algorithms" extension is
+     * not present in the ClientHello handshake message.
+     */
+    private static final
+            class CHSignatureSchemesAbsence implements HandshakeAbsence {
+        @Override
+        public void absent(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The comsuming happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
 
-        for (SignatureAndHashAlgorithm algorithm : algorithms) {
-            s.putInt8(algorithm.getHashValue());      // HashAlgorithm
-            s.putInt8(algorithm.getSignatureValue()); // SignatureAlgorithm
+            // This is a mandatory extension for certificate authentication
+            // in TLS 1.3.
+            //
+            // We may support the server authentication other than X.509
+            // certificate later.
+            if (shc.negotiatedProtocol.useTLS13PlusSpec()) {
+                shc.conContext.fatal(Alert.MISSING_EXTENSION,
+                    "No mandatory signature_algorithms extension in the " +
+                    "received CertificateRequest handshake message");
+            }
         }
     }
 
-    @Override
-    public String toString() {
-        StringBuilder sb = new StringBuilder();
-        boolean opened = false;
-        for (SignatureAndHashAlgorithm signAlg : algorithms) {
-            if (opened) {
-                sb.append(", " + signAlg.getAlgorithmName());
-            } else {
-                sb.append(signAlg.getAlgorithmName());
-                opened = true;
-            }
+    /**
+     * Network data producer of a "signature_algorithms" extension in
+     * the CertificateRequest handshake message.
+     */
+    private static final
+            class CRSignatureSchemesProducer implements HandshakeProducer {
+        // Prevent instantiation of this class.
+        private CRSignatureSchemesProducer() {
+            // blank
         }
 
-        return "Extension " + type + ", signature_algorithms: " + sb;
+        @Override
+        public byte[] produce(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The producing happens in server side only.
+            ServerHandshakeContext shc = (ServerHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            //
+            // Note that this is a mandatory extension for CertificateRequest
+            // handshake message in TLS 1.3.
+            if (!shc.sslConfig.isAvailable(
+                    SSLExtension.CR_SIGNATURE_ALGORITHMS)) {
+                shc.conContext.fatal(Alert.MISSING_EXTENSION,
+                        "No available signature_algorithms extension " +
+                        "for client certificate authentication");
+                return null;    // make the compiler happy
+            }
+
+            // Produce the extension.
+            if (shc.localSupportedSignAlgs == null) {
+                shc.localSupportedSignAlgs =
+                    SignatureScheme.getSupportedAlgorithms(
+                            shc.algorithmConstraints, shc.activeProtocols);
+            }
+
+            int vectorLen = SignatureScheme.sizeInRecord() *
+                    shc.localSupportedSignAlgs.size();
+            byte[] extData = new byte[vectorLen + 2];
+            ByteBuffer m = ByteBuffer.wrap(extData);
+            Record.putInt16(m, vectorLen);
+            for (SignatureScheme ss : shc.localSupportedSignAlgs) {
+                Record.putInt16(m, ss.id);
+            }
+
+            // Update the context.
+            shc.handshakeExtensions.put(
+                    SSLExtension.CR_SIGNATURE_ALGORITHMS,
+                    new SignatureSchemesSpec(shc.localSupportedSignAlgs));
+
+            return extData;
+        }
+    }
+
+    /**
+     * Network data consumer of a "signature_algorithms" extension in
+     * the CertificateRequest handshake message.
+     */
+    private static final
+            class CRSignatureSchemesConsumer implements ExtensionConsumer {
+        // Prevent instantiation of this class.
+        private CRSignatureSchemesConsumer() {
+            // blank
+        }
+        @Override
+        public void consume(ConnectionContext context,
+            HandshakeMessage message, ByteBuffer buffer) throws IOException {
+            // The comsuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // Is it a supported and enabled extension?
+            //
+            // Note that this is a mandatory extension for CertificateRequest
+            // handshake message in TLS 1.3.
+            if (!chc.sslConfig.isAvailable(
+                    SSLExtension.CR_SIGNATURE_ALGORITHMS)) {
+                chc.conContext.fatal(Alert.HANDSHAKE_FAILURE,
+                        "No available signature_algorithms extension " +
+                        "for client certificate authentication");
+                return;     // make the compiler happy
+            }
+
+            // Parse the extension.
+            SignatureSchemesSpec spec;
+            try {
+                spec = new SignatureSchemesSpec(buffer);
+            } catch (IOException ioe) {
+                chc.conContext.fatal(Alert.UNEXPECTED_MESSAGE, ioe);
+                return;     // fatal() always throws, make the compiler happy.
+            }
+
+            List<SignatureScheme> knownSignatureSchemes = new LinkedList<>();
+            for (int id : spec.signatureSchemes) {
+                SignatureScheme ss = SignatureScheme.valueOf(id);
+                if (ss != null) {
+                    knownSignatureSchemes.add(ss);
+                }
+            }
+
+            // Update the context.
+            // chc.peerRequestedSignatureSchemes = knownSignatureSchemes;
+            chc.handshakeExtensions.put(
+                    SSLExtension.CR_SIGNATURE_ALGORITHMS, spec);
+
+            // No impact on session resumption.
+        }
+    }
+
+    /**
+     * After session creation consuming of a "signature_algorithms"
+     * extension in the CertificateRequest handshake message.
+     */
+    private static final class CRSignatureSchemesUpdate
+            implements HandshakeConsumer {
+        // Prevent instantiation of this class.
+        private CRSignatureSchemesUpdate() {
+            // blank
+        }
+
+        @Override
+        public void consume(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The comsuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            SignatureSchemesSpec spec =
+                    (SignatureSchemesSpec)chc.handshakeExtensions.get(
+                            SSLExtension.CR_SIGNATURE_ALGORITHMS);
+            if (spec == null) {
+                // Ignore, no "signature_algorithms" extension requested.
+                return;
+            }
+
+            // update the context
+            List<SignatureScheme> shemes =
+                    SignatureScheme.getSupportedAlgorithms(
+                            chc.algorithmConstraints, chc.negotiatedProtocol,
+                            spec.signatureSchemes);
+            chc.peerRequestedSignatureSchemes = shemes;
+
+            // If no "signature_algorithms_cert" extension is present, then
+            // the "signature_algorithms" extension also applies to
+            // signatures appearing in certificates.
+            SignatureSchemesSpec certSpec =
+                    (SignatureSchemesSpec)chc.handshakeExtensions.get(
+                            SSLExtension.CH_SIGNATURE_ALGORITHMS_CERT);
+            if (certSpec == null) {
+                chc.peerRequestedCertSignSchemes = shemes;
+            }
+
+            chc.handshakeSession.setPeerSupportedSignatureAlgorithms(shemes);
+        }
+    }
+
+    /**
+     * The absence processing if a "signature_algorithms" extension is
+     * not present in the CertificateRequest handshake message.
+     */
+    private static final
+            class CRSignatureSchemesAbsence implements HandshakeAbsence {
+        @Override
+        public void absent(ConnectionContext context,
+                HandshakeMessage message) throws IOException {
+            // The comsuming happens in client side only.
+            ClientHandshakeContext chc = (ClientHandshakeContext)context;
+
+            // This is a mandatory extension for CertificateRequest handshake
+            // message in TLS 1.3.
+            chc.conContext.fatal(Alert.MISSING_EXTENSION,
+                    "No mandatory signature_algorithms extension in the " +
+                    "received CertificateRequest handshake message");
+        }
     }
 }
-