/* * 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.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.net.ssl.SSLException; import sun.security.ssl.SSLCipher.SSLReadCipher; import sun.security.ssl.SSLCipher.SSLWriteCipher; import sun.security.ssl.SSLHandshake.HandshakeMessage; import sun.security.ssl.SSLTrafficKeyDerivation.LegacyTrafficKeyDerivation; /** * Pack of the ChangeCipherSpec message. */ final class ChangeCipherSpec { static final SSLConsumer t10Consumer = new T10ChangeCipherSpecConsumer(); static final HandshakeProducer t10Producer = new T10ChangeCipherSpecProducer(); static final SSLConsumer t13Consumer = new T13ChangeCipherSpecConsumer(); /** * The "ChangeCipherSpec" message producer. */ private static final class T10ChangeCipherSpecProducer implements HandshakeProducer { // Prevent instantiation of this class. private T10ChangeCipherSpecProducer() { // blank } @Override public byte[] produce(ConnectionContext context, HandshakeMessage message) throws IOException { HandshakeContext hc = (HandshakeContext)context; SSLKeyDerivation kd = hc.handshakeKeyDerivation; if (!(kd instanceof LegacyTrafficKeyDerivation)) { throw new UnsupportedOperationException("Not supported yet."); } LegacyTrafficKeyDerivation tkd = (LegacyTrafficKeyDerivation)kd; CipherSuite ncs = hc.negotiatedCipherSuite; Authenticator writeAuthenticator; if (ncs.bulkCipher.cipherType == CipherType.AEAD_CIPHER) { writeAuthenticator = Authenticator.valueOf(hc.negotiatedProtocol); } else { try { writeAuthenticator = Authenticator.valueOf( hc.negotiatedProtocol, ncs.macAlg, tkd.getTrafficKey(hc.sslConfig.isClientMode ? "clientMacKey" : "serverMacKey")); } catch (NoSuchAlgorithmException | InvalidKeyException e) { // unlikely throw new SSLException("Algorithm missing: ", e); } } SecretKey writeKey = tkd.getTrafficKey(hc.sslConfig.isClientMode ? "clientWriteKey" : "serverWriteKey"); SecretKey writeIv = tkd.getTrafficKey(hc.sslConfig.isClientMode ? "clientWriteIv" : "serverWriteIv"); IvParameterSpec iv = (writeIv == null) ? null : new IvParameterSpec(writeIv.getEncoded()); SSLWriteCipher writeCipher; try { writeCipher = ncs.bulkCipher.createWriteCipher( writeAuthenticator, hc.negotiatedProtocol, writeKey, iv, hc.sslContext.getSecureRandom()); } catch (GeneralSecurityException gse) { // unlikely throw new SSLException("Algorithm missing: ", gse); } if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Produced ChangeCipherSpec message"); } hc.conContext.outputRecord.changeWriteCiphers(writeCipher, true); // The handshake message has been delivered. return null; } } /** * The "ChangeCipherSpec" message producer. */ private static final class T10ChangeCipherSpecConsumer implements SSLConsumer { // Prevent instantiation of this class. private T10ChangeCipherSpecConsumer() { // blank } @Override public void consume(ConnectionContext context, ByteBuffer message) throws IOException { TransportContext tc = (TransportContext)context; // This comsumer can be used only once. tc.consumers.remove(ContentType.CHANGE_CIPHER_SPEC.id); // parse if (message.remaining() != 1 || message.get() != 1) { tc.fatal(Alert.UNEXPECTED_MESSAGE, "Malformed or unexpected ChangeCipherSpec message"); } if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Consuming ChangeCipherSpec message"); } // validate if (tc.handshakeContext == null) { tc.fatal(Alert.HANDSHAKE_FAILURE, "Unexpected ChangeCipherSpec message"); } HandshakeContext hc = tc.handshakeContext; if (hc.handshakeKeyDerivation == null) { tc.fatal(Alert.UNEXPECTED_MESSAGE, "Unexpected ChangeCipherSpec message"); } SSLKeyDerivation kd = hc.handshakeKeyDerivation; if (kd instanceof LegacyTrafficKeyDerivation) { LegacyTrafficKeyDerivation tkd = (LegacyTrafficKeyDerivation)kd; CipherSuite ncs = hc.negotiatedCipherSuite; Authenticator readAuthenticator; if (ncs.bulkCipher.cipherType == CipherType.AEAD_CIPHER) { readAuthenticator = Authenticator.valueOf(hc.negotiatedProtocol); } else { try { readAuthenticator = Authenticator.valueOf( hc.negotiatedProtocol, ncs.macAlg, tkd.getTrafficKey(hc.sslConfig.isClientMode ? "serverMacKey" : "clientMacKey")); } catch (NoSuchAlgorithmException | InvalidKeyException e) { // unlikely throw new SSLException("Algorithm missing: ", e); } } SecretKey readKey = tkd.getTrafficKey(hc.sslConfig.isClientMode ? "serverWriteKey" : "clientWriteKey"); SecretKey readIv = tkd.getTrafficKey(hc.sslConfig.isClientMode ? "serverWriteIv" : "clientWriteIv"); IvParameterSpec iv = (readIv == null) ? null : new IvParameterSpec(readIv.getEncoded()); SSLReadCipher readCipher; try { readCipher = ncs.bulkCipher.createReadCipher( readAuthenticator, hc.negotiatedProtocol, readKey, iv, hc.sslContext.getSecureRandom()); } catch (GeneralSecurityException gse) { // unlikely throw new SSLException("Algorithm missing: ", gse); } tc.inputRecord.changeReadCiphers(readCipher); } else { throw new UnsupportedOperationException("Not supported yet."); } } } private static final class T13ChangeCipherSpecConsumer implements SSLConsumer { // Prevent instantiation of this class. private T13ChangeCipherSpecConsumer() { // blank } // An implementation may receive an unencrypted record of type // change_cipher_spec consisting of the single byte value 0x01 // at any time after the first ClientHello message has been // sent or received and before the peer's Finished message has // been received and MUST simply drop it without further // processing. @Override public void consume(ConnectionContext context, ByteBuffer message) throws IOException { TransportContext tc = (TransportContext)context; // This comsumer can be used only once. tc.consumers.remove(ContentType.CHANGE_CIPHER_SPEC.id); // parse if (message.remaining() != 1 || message.get() != 1) { tc.fatal(Alert.UNEXPECTED_MESSAGE, "Malformed or unexpected ChangeCipherSpec message"); } if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) { SSLLogger.fine("Consuming ChangeCipherSpec message"); } // no further processing } } }