1 /*
   2  * Copyright (c) 2018, 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.security.GeneralSecurityException;
  31 
  32 import sun.security.ssl.SSLHandshake.HandshakeMessage;
  33 import sun.security.ssl.SSLCipher.SSLReadCipher;
  34 import sun.security.ssl.SSLCipher.SSLWriteCipher;
  35 
  36 import javax.crypto.SecretKey;
  37 import javax.crypto.spec.IvParameterSpec;
  38 
  39 /**
  40  * Pack of the KeyUpdate handshake message.
  41  */
  42 final class KeyUpdate {
  43     static final SSLProducer kickstartProducer =
  44         new KeyUpdateKickstartProducer();
  45 
  46     static final SSLConsumer handshakeConsumer =
  47         new KeyUpdateConsumer();
  48     static final HandshakeProducer handshakeProducer =
  49         new KeyUpdateProducer();
  50 
  51     /**
  52      * The KeyUpdate handshake message.
  53      *
  54      * The KeyUpdate handshake message is used to indicate that the sender is
  55      * updating its sending cryptographic keys.
  56      *
  57      *       enum {
  58      *           update_not_requested(0), update_requested(1), (255)
  59      *       } KeyUpdateRequest;
  60      *
  61      *       struct {
  62      *           KeyUpdateRequest request_update;
  63      *       } KeyUpdate;
  64      */
  65     static final class KeyUpdateMessage extends HandshakeMessage {
  66         static final byte NOTREQUSTED = 0;
  67         static final byte REQUSTED = 1;
  68         private byte status;
  69         HandshakeOutStream hos;
  70 
  71         KeyUpdateMessage(HandshakeContext context, byte status) {
  72             super(context);
  73             this.status = status;
  74             if (status > 1) {
  75                 new IOException("KeyUpdate message value invalid: " + status);
  76             }
  77         }
  78         KeyUpdateMessage(HandshakeContext context, ByteBuffer m)
  79                 throws IOException{
  80             super(context);
  81 
  82             if (m.remaining() != 1) {
  83                 throw new IOException("KeyUpdate has an unexpected length of "+
  84                         m.remaining());
  85             }
  86 
  87             status = m.get();
  88             if (status > 1) {
  89                 new IOException("KeyUpdate message value invalid: " + status);
  90             }
  91         }
  92 
  93         KeyUpdateMessage(PostHandshakeContext context, byte status) {
  94             super(context);
  95             this.status = status;
  96             if (status > 1) {
  97                 new IOException("KeyUpdate message value invalid: " + status);
  98             }
  99         }
 100 
 101         @Override
 102         public SSLHandshake handshakeType() {
 103             return SSLHandshake.KEY_UPDATE;
 104         }
 105 
 106         @Override
 107         public int messageLength() {
 108             // one byte enum
 109             return 1;
 110         }
 111 
 112         @Override
 113         public void send(HandshakeOutStream s) throws IOException {
 114             s.write(status);
 115         }
 116 
 117         @Override
 118         public String toString() {
 119             throw new UnsupportedOperationException("Not supported yet.");
 120         }
 121 
 122         byte getStatus() {
 123             return status;
 124         }
 125     }
 126 
 127     private static final
 128             class KeyUpdateKickstartProducer implements SSLProducer {
 129         // Prevent instantiation of this class.
 130         private KeyUpdateKickstartProducer() {
 131             // blank
 132         }
 133 
 134         // Produce kickstart handshake message.
 135         @Override
 136         public byte[] produce(ConnectionContext context) throws IOException {
 137             HandshakeContext hc = (HandshakeContext)context;
 138             handshakeProducer.produce(hc,
 139                     new KeyUpdateMessage(hc, KeyUpdateMessage.REQUSTED));
 140             return null;
 141         }
 142     }
 143 
 144     /**
 145      * The "KeyUpdate" handshake message consumer.
 146      */
 147     private static final class KeyUpdateConsumer implements SSLConsumer {
 148         // Prevent instantiation of this class.
 149         private KeyUpdateConsumer() {
 150             // blank
 151         }
 152 
 153         @Override
 154         public void consume(ConnectionContext context,
 155                 ByteBuffer message) throws IOException {
 156             // The consuming happens in client side only.
 157             HandshakeContext hc = (HandshakeContext)context;
 158             KeyUpdateMessage km = new KeyUpdateMessage(hc, message);
 159 
 160             if (km.getStatus() == KeyUpdateMessage.NOTREQUSTED) {
 161                 return;
 162             }
 163 
 164             SSLTrafficKeyDerivation kdg =
 165                     SSLTrafficKeyDerivation.valueOf(hc.conContext.protocolVersion);
 166             if (kdg == null) {
 167                 // unlikely
 168                 hc.conContext.fatal(Alert.INTERNAL_ERROR,
 169                         "Not supported key derivation: " +
 170                                 hc.conContext.protocolVersion);
 171                 return;
 172             }
 173 
 174             SSLKeyDerivation skd = kdg.createKeyDerivation(hc,
 175                     hc.conContext.inputRecord.readCipher.baseSecret);
 176             SecretKey nplus1 = skd.deriveKey("TlsUpdateNplus1", null);
 177             if (skd == null) {
 178                 // unlikely
 179                 hc.conContext.fatal(Alert.INTERNAL_ERROR,
 180                         "no key derivation");
 181                 return;
 182             }
 183 
 184             SSLKeyDerivation kd = kdg.createKeyDerivation(hc, nplus1);
 185             SecretKey key = kd.deriveKey("TlsKey", null);
 186             IvParameterSpec ivSpec =
 187                     new IvParameterSpec(kd.deriveKey("TlsIv", null)
 188                             .getEncoded());
 189             try {
 190                 SSLReadCipher rc =
 191                         hc.negotiatedCipherSuite.bulkCipher.createReadCipher(
 192                                 Authenticator.valueOf(hc.conContext.protocolVersion),
 193                                 hc.conContext.protocolVersion, key, ivSpec,
 194                                 hc.sslContext.getSecureRandom());
 195                 hc.conContext.inputRecord.changeReadCiphers(rc);
 196                 hc.conContext.inputRecord.readCipher.baseSecret = nplus1;
 197                 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
 198                     SSLLogger.fine("KeyUpdate: read key updated");
 199                 }
 200 
 201             } catch (GeneralSecurityException e) {
 202                 throw new IOException(e);
 203             }
 204 
 205             // Send Reply
 206             handshakeProducer.produce(hc,
 207                     new KeyUpdateMessage(hc, KeyUpdateMessage.NOTREQUSTED));
 208         }
 209     }
 210 
 211     /**
 212      * The "KeyUpdate" handshake message producer.
 213      */
 214     private static final class KeyUpdateProducer implements HandshakeProducer {
 215         // Prevent instantiation of this class.
 216         private KeyUpdateProducer() {
 217             // blank
 218         }
 219 
 220         @Override
 221         public byte[] produce(ConnectionContext context,
 222                 HandshakeMessage message) throws IOException {
 223             // The producing happens in server side only.
 224             HandshakeContext hc = (HandshakeContext)context;
 225             KeyUpdateMessage km = (KeyUpdateMessage)message;
 226 
 227             SecretKey secret;
 228 
 229             if (km.getStatus() == KeyUpdateMessage.REQUSTED) {
 230                 secret = hc.conContext.outputRecord.writeCipher.baseSecret;
 231             } else {
 232                 km.write(hc.handshakeOutput);
 233                 hc.handshakeOutput.flush();
 234                 return null;
 235             }
 236 
 237             SSLTrafficKeyDerivation kdg =
 238                     SSLTrafficKeyDerivation.valueOf(hc.conContext.protocolVersion);
 239             if (kdg == null) {
 240                 // unlikely
 241                 hc.conContext.fatal(Alert.INTERNAL_ERROR,
 242                         "Not supported key derivation: " +
 243                                 hc.conContext.protocolVersion);
 244                 return null;
 245             }
 246             // update the application traffic read keys.
 247             SSLKeyDerivation skd = kdg.createKeyDerivation(hc, secret);
 248             if (skd == null) {
 249                 // unlikely
 250                 hc.conContext.fatal(Alert.INTERNAL_ERROR,"no key derivation");
 251                 return null;
 252             }
 253             SecretKey nplus1 = skd.deriveKey("TlsUpdateNplus1", null);
 254             SSLKeyDerivation kd =
 255                     kdg.createKeyDerivation(hc, nplus1);
 256             SecretKey key = kd.deriveKey("TlsKey", null);
 257             IvParameterSpec ivSpec =
 258                     new IvParameterSpec(kd.deriveKey("TlsIv", null)
 259                             .getEncoded());
 260 
 261             SSLWriteCipher wc;
 262             try {
 263                 wc = hc.negotiatedCipherSuite.bulkCipher.createWriteCipher(
 264                         Authenticator.valueOf(hc.conContext.protocolVersion),
 265                         hc.conContext.protocolVersion, key, ivSpec,
 266                         hc.sslContext.getSecureRandom());
 267 
 268             } catch (GeneralSecurityException gse){
 269                 hc.conContext.fatal(Alert.INTERNAL_ERROR,
 270                         "Failure to derive application secrets", gse);
 271                 return null;
 272             }
 273 
 274             km.write(hc.handshakeOutput);
 275             hc.handshakeOutput.flush();
 276             hc.conContext.outputRecord.changeWriteCiphers(wc, false);
 277             hc.conContext.outputRecord.writeCipher.baseSecret = nplus1;
 278             if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
 279                 SSLLogger.fine("KeyUpdate: write key updated");
 280             }
 281             return null;
 282         }
 283     }
 284 }