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 package sun.security.ssl;
  26 
  27 import java.io.IOException;
  28 import java.nio.ByteBuffer;
  29 import java.text.MessageFormat;
  30 import java.util.*;
  31 import sun.security.ssl.SSLExtension.ExtensionConsumer;
  32 
  33 import sun.security.ssl.SSLExtension.SSLExtensionSpec;
  34 import sun.security.ssl.SSLHandshake.HandshakeMessage;
  35 
  36 /**
  37  * Pack of the "psk_key_exchange_modes" extensions.
  38  */
  39 final class PskKeyExchangeModesExtension {
  40     static final HandshakeProducer chNetworkProducer =
  41             new PskKeyExchangeModesProducer();
  42     static final ExtensionConsumer chOnLoadConsumer =
  43             new PskKeyExchangeModesConsumer();
  44 
  45     enum PskKeyExchangeMode {
  46         PSK_KE(0),
  47         PSK_DHE_KE(1);
  48 
  49         private final int v;
  50 
  51         PskKeyExchangeMode(int v) {
  52             this.v = v;
  53         }
  54 
  55         static PskKeyExchangeMode ofInt(int v) {
  56             for(PskKeyExchangeMode mode : values()) {
  57                 if (mode.v == v) {
  58                     return mode;
  59                 }
  60             }
  61 
  62             return null;
  63         }
  64     }
  65 
  66     static final class PskKeyExchangeModesSpec implements SSLExtensionSpec {
  67 
  68 
  69         final List<PskKeyExchangeMode> modes;
  70 
  71         PskKeyExchangeModesSpec(List<PskKeyExchangeMode> modes) {
  72             this.modes = modes;
  73         }
  74 
  75         PskKeyExchangeModesSpec(ByteBuffer m) throws IOException {
  76 
  77             modes = new ArrayList<>();
  78             int modesEncodedLength = Record.getInt8(m);
  79             int modesReadLength = 0;
  80             while (modesReadLength < modesEncodedLength) {
  81                 int mode = Record.getInt8(m);
  82                 // TODO: handle incorrect values
  83                 modes.add(PskKeyExchangeMode.ofInt(mode));
  84                 modesReadLength += 1;
  85             }
  86         }
  87 
  88         byte[] getEncoded() throws IOException {
  89 
  90             int encodedLength = modes.size() + 1;
  91             byte[] buffer = new byte[encodedLength];
  92             ByteBuffer m = ByteBuffer.wrap(buffer);
  93             Record.putInt8(m, modes.size());
  94             for(PskKeyExchangeMode curMode : modes) {
  95                 Record.putInt8(m, curMode.v);
  96             }
  97 
  98             return buffer;
  99         }
 100 
 101         @Override
 102         public String toString() {
 103             MessageFormat messageFormat = new MessageFormat(
 104             "\"PskKeyExchangeModes\": '{'\n" +
 105             "  \"ke_modes\"      : \"{0}\",\n" +
 106             "'}'",
 107             Locale.ENGLISH);
 108 
 109             Object[] messageFields = {
 110             Utilities.indent(modesString()),
 111             };
 112 
 113             return messageFormat.format(messageFields);
 114         }
 115 
 116         String modesString() {
 117             StringBuilder result = new StringBuilder();
 118             for(PskKeyExchangeMode curMode : modes) {
 119                 result.append(curMode.toString() + "\n");
 120             }
 121 
 122             return result.toString();
 123         }
 124     }
 125 
 126 
 127     private static final
 128             class PskKeyExchangeModesConsumer implements ExtensionConsumer {
 129         // Prevent instantiation of this class.
 130         private PskKeyExchangeModesConsumer() {
 131             // blank
 132         }
 133 
 134         @Override
 135         public void consume(ConnectionContext context,
 136             HandshakeMessage message, ByteBuffer buffer) throws IOException {
 137 
 138             ServerHandshakeContext shc =
 139                     (ServerHandshakeContext) message.handshakeContext;
 140 
 141             PskKeyExchangeModesSpec modes = new PskKeyExchangeModesSpec(buffer);
 142             if (SSLLogger.isOn && SSLLogger.isOn("ssl,handshake")) {
 143                 SSLLogger.fine(
 144                 "Received PskKeyExchangeModes extension: ", modes);
 145             }
 146 
 147             shc.pskKeyExchangeModes = modes.modes;
 148         }
 149 
 150     }
 151 
 152     private static final
 153             class PskKeyExchangeModesProducer implements HandshakeProducer {
 154 
 155         static final List<PskKeyExchangeMode> MODES =
 156             List.of(PskKeyExchangeMode.PSK_DHE_KE);
 157         static final PskKeyExchangeModesSpec MODES_MSG =
 158             new PskKeyExchangeModesSpec(MODES);
 159 
 160         // Prevent instantiation of this class.
 161         private PskKeyExchangeModesProducer() {
 162             // blank
 163         }
 164 
 165         @Override
 166         public byte[] produce(ConnectionContext context,
 167                 HandshakeMessage message) throws IOException {
 168 
 169             return MODES_MSG.getEncoded();
 170         }
 171 
 172     }
 173 }
 174