/* * 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.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(); 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 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]; } } 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; } @Override public String toString() { MessageFormat messageFormat = new MessageFormat( "\"signature schemes\": '['{0}']'", Locale.ENGLISH); if (signatureSchemes == null || signatureSchemes.length == 0) { Object[] messageFields = { "" }; return messageFormat.format(messageFields); } else { StringBuilder builder = new StringBuilder(512); boolean isFirst = true; for (int pv : signatureSchemes) { if (isFirst) { isFirst = false; } else { builder.append(", "); } builder.append(SignatureScheme.nameOf(pv)); } Object[] messageFields = { builder.toString() }; return messageFormat.format(messageFields); } } } 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(); } } } /** * 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 } @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); } 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 } @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. } } /** * 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; } // update the context List 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); } } } /** * 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; // 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"); } } } /** * 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 } @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 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 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"); } } }