Path: blob/master/test/jdk/sun/security/ssl/ALPN/AlpnGreaseTest.java
41152 views
/*1* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223// SunJSSE does not support dynamic system properties, no way to re-use24// system properties in samevm/agentvm mode.2526/*27* @test28* @bug 825463129* @summary Better support ALPN byte wire values in SunJSSE30* @library /javax/net/ssl/templates31* @run main/othervm AlpnGreaseTest32*/33import javax.net.ssl.*;34import javax.net.ssl.SSLEngineResult.HandshakeStatus;35import java.nio.ByteBuffer;36import java.nio.charset.StandardCharsets;37import java.util.Arrays;3839/**40* A SSLEngine usage example which simplifies the presentation41* by removing the I/O and multi-threading concerns.42*43* The test creates two SSLEngines, simulating a client and server.44* The "transport" layer consists two byte buffers: think of them45* as directly connected pipes.46*47* Note, this is a *very* simple example: real code will be much more48* involved. For example, different threading and I/O models could be49* used, transport mechanisms could close unexpectedly, and so on.50*51* When this application runs, notice that several messages52* (wrap/unwrap) pass before any application data is consumed or53* produced.54*/55public class AlpnGreaseTest implements SSLContextTemplate {5657private final SSLEngine clientEngine; // client Engine58private final ByteBuffer clientOut; // write side of clientEngine59private final ByteBuffer clientIn; // read side of clientEngine6061private final SSLEngine serverEngine; // server Engine62private final ByteBuffer serverOut; // write side of serverEngine63private final ByteBuffer serverIn; // read side of serverEngine6465// For data transport, this example uses local ByteBuffers. This66// isn't really useful, but the purpose of this example is to show67// SSLEngine concepts, not how to do network transport.68private final ByteBuffer cTOs; // "reliable" transport client->server69private final ByteBuffer sTOc; // "reliable" transport server->client7071// These are the various 8-bit char values that could be sent as GREASE72// values. We'll just make one big String here to make it easy to check73// that the right values are being output.74private static final byte[] greaseBytes = new byte[] {75(byte) 0x0A, (byte) 0x1A, (byte) 0x2A, (byte) 0x3A,76(byte) 0x4A, (byte) 0x5A, (byte) 0x6A, (byte) 0x7A,77(byte) 0x8A, (byte) 0x9A, (byte) 0xAA, (byte) 0xBA,78(byte) 0xCA, (byte) 0xDA, (byte) 0xEA, (byte) 0xFA79};8081private static final String greaseString =82new String(greaseBytes, StandardCharsets.ISO_8859_1);8384private static void findGreaseInClientHello(byte[] bytes) throws Exception {85for (int i = 0; i < bytes.length - greaseBytes.length; i++) {86if (Arrays.equals(bytes, i, i + greaseBytes.length,87greaseBytes, 0, greaseBytes.length)) {88System.out.println("Found greaseBytes in ClientHello at: " + i);89return;90}91}92throw new Exception("Couldn't find greaseBytes");93}9495private AlpnGreaseTest() throws Exception {96serverEngine = configureServerEngine(97createServerSSLContext().createSSLEngine());9899clientEngine = configureClientEngine(100createClientSSLContext().createSSLEngine());101102// We'll assume the buffer sizes are the same103// between client and server.104SSLSession session = clientEngine.getSession();105int appBufferMax = session.getApplicationBufferSize();106int netBufferMax = session.getPacketBufferSize();107108// We'll make the input buffers a bit bigger than the max needed109// size, so that unwrap()s following a successful data transfer110// won't generate BUFFER_OVERFLOWS.111//112// We'll use a mix of direct and indirect ByteBuffers for113// tutorial purposes only. In reality, only use direct114// ByteBuffers when they give a clear performance enhancement.115clientIn = ByteBuffer.allocate(appBufferMax + 50);116serverIn = ByteBuffer.allocate(appBufferMax + 50);117118cTOs = ByteBuffer.allocateDirect(netBufferMax);119sTOc = ByteBuffer.allocateDirect(netBufferMax);120121clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());122serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());123}124125//126// Protected methods could be used to customize the test case.127//128129/*130* Configure the client side engine.131*/132protected SSLEngine configureClientEngine(SSLEngine clientEngine) {133clientEngine.setUseClientMode(true);134135// Get/set parameters if needed136SSLParameters paramsClient = clientEngine.getSSLParameters();137paramsClient.setApplicationProtocols(new String[] { greaseString });138139clientEngine.setSSLParameters(paramsClient);140141return clientEngine;142}143144/*145* Configure the server side engine.146*/147protected SSLEngine configureServerEngine(SSLEngine serverEngine) {148serverEngine.setUseClientMode(false);149serverEngine.setNeedClientAuth(true);150151// Get/set parameters if needed152//153SSLParameters paramsServer = serverEngine.getSSLParameters();154paramsServer.setApplicationProtocols(new String[] { greaseString });155serverEngine.setSSLParameters(paramsServer);156157return serverEngine;158}159160public static void main(String[] args) throws Exception {161new AlpnGreaseTest().runTest();162}163164//165// Private methods that used to build the common part of the test.166//167168private void runTest() throws Exception {169SSLEngineResult clientResult;170SSLEngineResult serverResult;171172boolean dataDone = false;173boolean firstClientWrap = true;174while (isOpen(clientEngine) || isOpen(serverEngine)) {175log("=================");176177// client wrap178log("---Client Wrap---");179clientResult = clientEngine.wrap(clientOut, cTOs);180logEngineStatus(clientEngine, clientResult);181runDelegatedTasks(clientEngine);182183if (firstClientWrap) {184firstClientWrap = false;185byte[] bytes = new byte[cTOs.position()];186cTOs.duplicate().flip().get(bytes);187findGreaseInClientHello(bytes);188}189190// server wrap191log("---Server Wrap---");192serverResult = serverEngine.wrap(serverOut, sTOc);193logEngineStatus(serverEngine, serverResult);194runDelegatedTasks(serverEngine);195196cTOs.flip();197sTOc.flip();198199// client unwrap200log("---Client Unwrap---");201clientResult = clientEngine.unwrap(sTOc, clientIn);202logEngineStatus(clientEngine, clientResult);203runDelegatedTasks(clientEngine);204205// server unwrap206log("---Server Unwrap---");207serverResult = serverEngine.unwrap(cTOs, serverIn);208logEngineStatus(serverEngine, serverResult);209runDelegatedTasks(serverEngine);210211cTOs.compact();212sTOc.compact();213214// After we've transferred all application data between the client215// and server, we close the clientEngine's outbound stream.216// This generates a close_notify handshake message, which the217// server engine receives and responds by closing itself.218if (!dataDone && (clientOut.limit() == serverIn.position()) &&219(serverOut.limit() == clientIn.position())) {220221// Check ALPN Value222String alpnServerValue = serverEngine.getApplicationProtocol();223String alpnClientValue = clientEngine.getApplicationProtocol();224225if (!alpnServerValue.equals(greaseString)226|| !alpnClientValue.equals(greaseString)) {227throw new Exception("greaseString didn't match");228}229230// A sanity check to ensure we got what was sent.231checkTransfer(serverOut, clientIn);232checkTransfer(clientOut, serverIn);233234log("\tClosing clientEngine's *OUTBOUND*...");235clientEngine.closeOutbound();236logEngineStatus(clientEngine);237238dataDone = true;239log("\tClosing serverEngine's *OUTBOUND*...");240serverEngine.closeOutbound();241logEngineStatus(serverEngine);242}243}244}245246private static boolean isOpen(SSLEngine engine) {247return (!engine.isOutboundDone() || !engine.isInboundDone());248}249250private static void logEngineStatus(SSLEngine engine) {251log("\tCurrent HS State: " + engine.getHandshakeStatus());252log("\tisInboundDone() : " + engine.isInboundDone());253log("\tisOutboundDone(): " + engine.isOutboundDone());254}255256private static void logEngineStatus(257SSLEngine engine, SSLEngineResult result) {258log("\tResult Status : " + result.getStatus());259log("\tResult HS Status : " + result.getHandshakeStatus());260log("\tEngine HS Status : " + engine.getHandshakeStatus());261log("\tisInboundDone() : " + engine.isInboundDone());262log("\tisOutboundDone() : " + engine.isOutboundDone());263log("\tMore Result : " + result);264}265266private static void log(String message) {267System.err.println(message);268}269270// If the result indicates that we have outstanding tasks to do,271// go ahead and run them in this thread.272private static void runDelegatedTasks(SSLEngine engine) throws Exception {273if (engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {274Runnable runnable;275while ((runnable = engine.getDelegatedTask()) != null) {276log(" running delegated task...");277runnable.run();278}279HandshakeStatus hsStatus = engine.getHandshakeStatus();280if (hsStatus == HandshakeStatus.NEED_TASK) {281throw new Exception(282"handshake shouldn't need additional tasks");283}284logEngineStatus(engine);285}286}287288// Simple check to make sure everything came across as expected.289private static void checkTransfer(ByteBuffer a, ByteBuffer b)290throws Exception {291a.flip();292b.flip();293294if (!a.equals(b)) {295throw new Exception("Data didn't transfer cleanly");296} else {297log("\tData transferred cleanly");298}299300a.position(a.limit());301b.position(b.limit());302a.limit(a.capacity());303b.limit(b.capacity());304}305}306307308