1 /* 2 * Copyright (c) 2020, 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 // SunJSSE does not support dynamic system properties, no way to re-use 25 // system properties in samevm/agentvm mode. 26 27 /* 28 * @test 29 * @bug 8254631 30 * @summary Better support ALPN byte wire values in SunJSSE 31 * @library /javax/net/ssl/templates 32 * @run main/othervm AlpnGreaseTest 33 */ 34 import javax.net.ssl.*; 35 import javax.net.ssl.SSLEngineResult.HandshakeStatus; 36 import java.nio.ByteBuffer; 37 import java.nio.charset.StandardCharsets; 38 import java.util.Arrays; 39 40 /** 41 * A SSLEngine usage example which simplifies the presentation 42 * by removing the I/O and multi-threading concerns. 43 * 44 * The test creates two SSLEngines, simulating a client and server. 45 * The "transport" layer consists two byte buffers: think of them 46 * as directly connected pipes. 47 * 48 * Note, this is a *very* simple example: real code will be much more 49 * involved. For example, different threading and I/O models could be 50 * used, transport mechanisms could close unexpectedly, and so on. 51 * 52 * When this application runs, notice that several messages 53 * (wrap/unwrap) pass before any application data is consumed or 54 * produced. 55 */ 56 public class AlpnGreaseTest implements SSLContextTemplate { 57 58 private final SSLEngine clientEngine; // client Engine 59 private final ByteBuffer clientOut; // write side of clientEngine 60 private final ByteBuffer clientIn; // read side of clientEngine 61 62 private final SSLEngine serverEngine; // server Engine 63 private final ByteBuffer serverOut; // write side of serverEngine 64 private final ByteBuffer serverIn; // read side of serverEngine 65 66 // For data transport, this example uses local ByteBuffers. This 67 // isn't really useful, but the purpose of this example is to show 68 // SSLEngine concepts, not how to do network transport. 69 private final ByteBuffer cTOs; // "reliable" transport client->server 70 private final ByteBuffer sTOc; // "reliable" transport server->client 71 72 // These are the various 8-bit char values that could be sent as GREASE 73 // values. We'll just make one big String here to make it easy to check 74 // that the right values are being output. 75 private static final byte[] greaseBytes = new byte[] { 76 (byte) 0x0A, (byte) 0x1A, (byte) 0x2A, (byte) 0x3A, 77 (byte) 0x4A, (byte) 0x5A, (byte) 0x6A, (byte) 0x7A, 78 (byte) 0x8A, (byte) 0x9A, (byte) 0xAA, (byte) 0xBA, 79 (byte) 0xCA, (byte) 0xDA, (byte) 0xEA, (byte) 0xFA 80 }; 81 82 private static final String greaseString = 83 new String(greaseBytes, StandardCharsets.ISO_8859_1); 84 findGreaseInClientHello(byte[] bytes)85 private static void findGreaseInClientHello(byte[] bytes) throws Exception { 86 for (int i = 0; i < bytes.length - greaseBytes.length; i++) { 87 if (Arrays.equals(bytes, i, i + greaseBytes.length, 88 greaseBytes, 0, greaseBytes.length)) { 89 System.out.println("Found greaseBytes in ClientHello at: " + i); 90 return; 91 } 92 } 93 throw new Exception("Couldn't find greaseBytes"); 94 } 95 AlpnGreaseTest()96 private AlpnGreaseTest() throws Exception { 97 serverEngine = configureServerEngine( 98 createServerSSLContext().createSSLEngine()); 99 100 clientEngine = configureClientEngine( 101 createClientSSLContext().createSSLEngine()); 102 103 // We'll assume the buffer sizes are the same 104 // between client and server. 105 SSLSession session = clientEngine.getSession(); 106 int appBufferMax = session.getApplicationBufferSize(); 107 int netBufferMax = session.getPacketBufferSize(); 108 109 // We'll make the input buffers a bit bigger than the max needed 110 // size, so that unwrap()s following a successful data transfer 111 // won't generate BUFFER_OVERFLOWS. 112 // 113 // We'll use a mix of direct and indirect ByteBuffers for 114 // tutorial purposes only. In reality, only use direct 115 // ByteBuffers when they give a clear performance enhancement. 116 clientIn = ByteBuffer.allocate(appBufferMax + 50); 117 serverIn = ByteBuffer.allocate(appBufferMax + 50); 118 119 cTOs = ByteBuffer.allocateDirect(netBufferMax); 120 sTOc = ByteBuffer.allocateDirect(netBufferMax); 121 122 clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes()); 123 serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes()); 124 } 125 126 // 127 // Protected methods could be used to customize the test case. 128 // 129 130 /* 131 * Configure the client side engine. 132 */ configureClientEngine(SSLEngine clientEngine)133 protected SSLEngine configureClientEngine(SSLEngine clientEngine) { 134 clientEngine.setUseClientMode(true); 135 136 // Get/set parameters if needed 137 SSLParameters paramsClient = clientEngine.getSSLParameters(); 138 paramsClient.setApplicationProtocols(new String[] { greaseString }); 139 140 clientEngine.setSSLParameters(paramsClient); 141 142 return clientEngine; 143 } 144 145 /* 146 * Configure the server side engine. 147 */ configureServerEngine(SSLEngine serverEngine)148 protected SSLEngine configureServerEngine(SSLEngine serverEngine) { 149 serverEngine.setUseClientMode(false); 150 serverEngine.setNeedClientAuth(true); 151 152 // Get/set parameters if needed 153 // 154 SSLParameters paramsServer = serverEngine.getSSLParameters(); 155 paramsServer.setApplicationProtocols(new String[] { greaseString }); 156 serverEngine.setSSLParameters(paramsServer); 157 158 return serverEngine; 159 } 160 main(String[] args)161 public static void main(String[] args) throws Exception { 162 new AlpnGreaseTest().runTest(); 163 } 164 165 // 166 // Private methods that used to build the common part of the test. 167 // 168 runTest()169 private void runTest() throws Exception { 170 SSLEngineResult clientResult; 171 SSLEngineResult serverResult; 172 173 boolean dataDone = false; 174 boolean firstClientWrap = true; 175 while (isOpen(clientEngine) || isOpen(serverEngine)) { 176 log("================="); 177 178 // client wrap 179 log("---Client Wrap---"); 180 clientResult = clientEngine.wrap(clientOut, cTOs); 181 logEngineStatus(clientEngine, clientResult); 182 runDelegatedTasks(clientEngine); 183 184 if (firstClientWrap) { 185 firstClientWrap = false; 186 byte[] bytes = new byte[cTOs.position()]; 187 cTOs.duplicate().flip().get(bytes); 188 findGreaseInClientHello(bytes); 189 } 190 191 // server wrap 192 log("---Server Wrap---"); 193 serverResult = serverEngine.wrap(serverOut, sTOc); 194 logEngineStatus(serverEngine, serverResult); 195 runDelegatedTasks(serverEngine); 196 197 cTOs.flip(); 198 sTOc.flip(); 199 200 // client unwrap 201 log("---Client Unwrap---"); 202 clientResult = clientEngine.unwrap(sTOc, clientIn); 203 logEngineStatus(clientEngine, clientResult); 204 runDelegatedTasks(clientEngine); 205 206 // server unwrap 207 log("---Server Unwrap---"); 208 serverResult = serverEngine.unwrap(cTOs, serverIn); 209 logEngineStatus(serverEngine, serverResult); 210 runDelegatedTasks(serverEngine); 211 212 cTOs.compact(); 213 sTOc.compact(); 214 215 // After we've transferred all application data between the client 216 // and server, we close the clientEngine's outbound stream. 217 // This generates a close_notify handshake message, which the 218 // server engine receives and responds by closing itself. 219 if (!dataDone && (clientOut.limit() == serverIn.position()) && 220 (serverOut.limit() == clientIn.position())) { 221 222 // Check ALPN Value 223 String alpnServerValue = serverEngine.getApplicationProtocol(); 224 String alpnClientValue = clientEngine.getApplicationProtocol(); 225 226 if (!alpnServerValue.equals(greaseString) 227 || !alpnClientValue.equals(greaseString)) { 228 throw new Exception("greaseString didn't match"); 229 } 230 231 // A sanity check to ensure we got what was sent. 232 checkTransfer(serverOut, clientIn); 233 checkTransfer(clientOut, serverIn); 234 235 log("\tClosing clientEngine's *OUTBOUND*..."); 236 clientEngine.closeOutbound(); 237 logEngineStatus(clientEngine); 238 239 dataDone = true; 240 log("\tClosing serverEngine's *OUTBOUND*..."); 241 serverEngine.closeOutbound(); 242 logEngineStatus(serverEngine); 243 } 244 } 245 } 246 isOpen(SSLEngine engine)247 private static boolean isOpen(SSLEngine engine) { 248 return (!engine.isOutboundDone() || !engine.isInboundDone()); 249 } 250 logEngineStatus(SSLEngine engine)251 private static void logEngineStatus(SSLEngine engine) { 252 log("\tCurrent HS State: " + engine.getHandshakeStatus()); 253 log("\tisInboundDone() : " + engine.isInboundDone()); 254 log("\tisOutboundDone(): " + engine.isOutboundDone()); 255 } 256 logEngineStatus( SSLEngine engine, SSLEngineResult result)257 private static void logEngineStatus( 258 SSLEngine engine, SSLEngineResult result) { 259 log("\tResult Status : " + result.getStatus()); 260 log("\tResult HS Status : " + result.getHandshakeStatus()); 261 log("\tEngine HS Status : " + engine.getHandshakeStatus()); 262 log("\tisInboundDone() : " + engine.isInboundDone()); 263 log("\tisOutboundDone() : " + engine.isOutboundDone()); 264 log("\tMore Result : " + result); 265 } 266 log(String message)267 private static void log(String message) { 268 System.err.println(message); 269 } 270 271 // If the result indicates that we have outstanding tasks to do, 272 // go ahead and run them in this thread. runDelegatedTasks(SSLEngine engine)273 private static void runDelegatedTasks(SSLEngine engine) throws Exception { 274 if (engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { 275 Runnable runnable; 276 while ((runnable = engine.getDelegatedTask()) != null) { 277 log(" running delegated task..."); 278 runnable.run(); 279 } 280 HandshakeStatus hsStatus = engine.getHandshakeStatus(); 281 if (hsStatus == HandshakeStatus.NEED_TASK) { 282 throw new Exception( 283 "handshake shouldn't need additional tasks"); 284 } 285 logEngineStatus(engine); 286 } 287 } 288 289 // Simple check to make sure everything came across as expected. checkTransfer(ByteBuffer a, ByteBuffer b)290 private static void checkTransfer(ByteBuffer a, ByteBuffer b) 291 throws Exception { 292 a.flip(); 293 b.flip(); 294 295 if (!a.equals(b)) { 296 throw new Exception("Data didn't transfer cleanly"); 297 } else { 298 log("\tData transferred cleanly"); 299 } 300 301 a.position(a.limit()); 302 b.position(b.limit()); 303 a.limit(a.capacity()); 304 b.limit(b.capacity()); 305 } 306 } 307