| /* | |
| * Copyright (c) 2003, 2024, 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. | |
| * | |
| * 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. | |
| */ | |
| /* | |
| * @test | |
| * @bug 7126889 | |
| * @summary Incorrect SSLEngine debug output | |
| * @library /test/lib /javax/net/ssl/templates | |
| * @run main DebugReportsOneExtraByte | |
| */ | |
| /* | |
| * Debug output was reporting n+1 bytes of data was written when it was | |
| * really was n. | |
| * | |
| * SunJSSE does not support dynamic system properties, no way to re-use | |
| * system properties in samevm/agentvm mode. | |
| */ | |
| /** | |
| * A SSLEngine usage example which simplifies the presentation | |
| * by removing the I/O and multi-threading concerns. | |
| * | |
| * The test creates two SSLEngines, simulating a client and server. | |
| * The "transport" layer consists two byte buffers: think of them | |
| * as directly connected pipes. | |
| * | |
| * Note, this is a *very* simple example: real code will be much more | |
| * involved. For example, different threading and I/O models could be | |
| * used, transport mechanisms could close unexpectedly, and so on. | |
| * | |
| * When this application runs, notice that several messages | |
| * (wrap/unwrap) pass before any application data is consumed or | |
| * produced. (For more information, please see the SSL/TLS | |
| * specifications.) There may several steps for a successful handshake, | |
| * so it's typical to see the following series of operations: | |
| * | |
| * client server message | |
| * ====== ====== ======= | |
| * wrap() ... ClientHello | |
| * ... unwrap() ClientHello | |
| * ... wrap() ServerHello/Certificate | |
| * unwrap() ... ServerHello/Certificate | |
| * wrap() ... ClientKeyExchange | |
| * wrap() ... ChangeCipherSpec | |
| * wrap() ... Finished | |
| * ... unwrap() ClientKeyExchange | |
| * ... unwrap() ChangeCipherSpec | |
| * ... unwrap() Finished | |
| * ... wrap() ChangeCipherSpec | |
| * ... wrap() Finished | |
| * unwrap() ... ChangeCipherSpec | |
| * unwrap() ... Finished | |
| */ | |
| import javax.net.ssl.*; | |
| import javax.net.ssl.SSLEngineResult.*; | |
| import java.nio.*; | |
| import jdk.test.lib.process.OutputAnalyzer; | |
| import jdk.test.lib.process.ProcessTools; | |
| import jdk.test.lib.security.SecurityUtils; | |
| public class DebugReportsOneExtraByte extends SSLEngineTemplate { | |
| /* | |
| * Enables logging of the SSLEngine operations. | |
| */ | |
| private static boolean logging = true; | |
| /* | |
| * Main entry point for this test. | |
| */ | |
| public static void main(String args[]) throws Exception { | |
| if (args.length == 0) { | |
| OutputAnalyzer output = ProcessTools.executeTestJava( | |
| "-Dtest.src=" + System.getProperty("test.src"), | |
| "-Djavax.net.debug=all", "DebugReportsOneExtraByte", "p"); | |
| output.shouldContain("WRITE: TLSv1 application_data, length = 8"); | |
| System.out.println("Test Passed."); | |
| } else { | |
| // Re-enable TLSv1 and TLS_RSA_* since test depends on it | |
| SecurityUtils.removeFromDisabledTlsAlgs("TLSv1", "TLS_RSA_*"); | |
| DebugReportsOneExtraByte test = new DebugReportsOneExtraByte(); | |
| test.runTest(); | |
| } | |
| } | |
| /* | |
| * Create an initialized SSLContext to use for these tests. | |
| */ | |
| public DebugReportsOneExtraByte() throws Exception { | |
| super(); | |
| } | |
| @Override | |
| protected SSLEngine configureServerEngine(SSLEngine serverEngine) { | |
| serverEngine.setUseClientMode(false); | |
| // Force a block-oriented ciphersuite. | |
| serverEngine.setEnabledCipherSuites( | |
| new String [] {"TLS_RSA_WITH_AES_128_CBC_SHA"}); | |
| return serverEngine; | |
| } | |
| @Override | |
| protected ContextParameters getClientContextParameters() { | |
| return new ContextParameters("TLSv1", "PKIX", "NewSunX509"); | |
| } | |
| @Override | |
| protected ContextParameters getServerContextParameters() { | |
| return new ContextParameters("TLSv1", "PKIX", "NewSunX509"); | |
| } | |
| @Override | |
| protected ByteBuffer createClientOutputBuffer() { | |
| // No need to write anything on the client side, it will | |
| // just confuse the output. | |
| return ByteBuffer.wrap("".getBytes()); | |
| } | |
| @Override | |
| protected ByteBuffer createServerOutputBuffer() { | |
| return ByteBuffer.wrap("Hi Client!".getBytes()); | |
| } | |
| /* | |
| * Run the test. | |
| * | |
| * Sit in a tight loop, both engines calling wrap/unwrap regardless | |
| * of whether data is available or not. We do this until both engines | |
| * report back they are closed. | |
| * | |
| * The main loop handles all of the I/O phases of the SSLEngine's | |
| * lifetime: | |
| * | |
| * initial handshaking | |
| * application data transfer | |
| * engine closing | |
| * | |
| * One could easily separate these phases into separate | |
| * sections of code. | |
| */ | |
| private void runTest() throws Exception { | |
| boolean dataDone = false; | |
| SSLEngineResult clientResult; // results from client's last operation | |
| SSLEngineResult serverResult; // results from server's last operation | |
| /* | |
| * Examining the SSLEngineResults could be much more involved, | |
| * and may alter the overall flow of the application. | |
| * | |
| * For example, if we received a BUFFER_OVERFLOW when trying | |
| * to write to the output pipe, we could reallocate a larger | |
| * pipe, but instead we wait for the peer to drain it. | |
| */ | |
| /* | |
| * Write one byte in first application packet, the rest | |
| * will come later. | |
| */ | |
| serverOut.limit(1); | |
| while (!isEngineClosed(clientEngine) || | |
| !isEngineClosed(serverEngine)) { | |
| log("================"); | |
| clientResult = clientEngine.wrap(clientOut, cTOs); | |
| log("client wrap: ", clientResult); | |
| runDelegatedTasks(clientEngine); | |
| serverResult = serverEngine.wrap(serverOut, sTOc); | |
| log("server wrap: ", serverResult); | |
| runDelegatedTasks(serverEngine); | |
| // Next wrap will split. | |
| if (serverOut.position() == 1) { | |
| serverOut.limit(serverOut.capacity()); | |
| } | |
| cTOs.flip(); | |
| sTOc.flip(); | |
| log("----"); | |
| clientResult = clientEngine.unwrap(sTOc, clientIn); | |
| log("client unwrap: ", clientResult); | |
| runDelegatedTasks(clientEngine); | |
| serverResult = serverEngine.unwrap(cTOs, serverIn); | |
| log("server unwrap: ", serverResult); | |
| runDelegatedTasks(serverEngine); | |
| cTOs.compact(); | |
| sTOc.compact(); | |
| /* | |
| * After we've transfered all application data between the client | |
| * and server, we close the clientEngine's outbound stream. | |
| * This generates a close_notify handshake message, which the | |
| * server engine receives and responds by closing itself. | |
| */ | |
| if (!dataDone && (clientOut.limit() == serverIn.position()) && | |
| (serverOut.limit() == clientIn.position())) { | |
| /* | |
| * A sanity check to ensure we got what was sent. | |
| */ | |
| checkTransfer(serverOut, clientIn); | |
| checkTransfer(clientOut, serverIn); | |
| log("\tClosing clientEngine's *OUTBOUND*..."); | |
| clientEngine.closeOutbound(); | |
| dataDone = true; | |
| } | |
| } | |
| } | |
| private static boolean isEngineClosed(SSLEngine engine) { | |
| return (engine.isOutboundDone() && engine.isInboundDone()); | |
| } | |
| /* | |
| * Logging code | |
| */ | |
| private static boolean resultOnce = true; | |
| private static void log(String str, SSLEngineResult result) { | |
| if (!logging) { | |
| return; | |
| } | |
| if (resultOnce) { | |
| resultOnce = false; | |
| System.out.println("The format of the SSLEngineResult is: \n" + | |
| "\t\"getStatus() / getHandshakeStatus()\" +\n" + | |
| "\t\"bytesConsumed() / bytesProduced()\"\n"); | |
| } | |
| HandshakeStatus hsStatus = result.getHandshakeStatus(); | |
| log(str + | |
| result.getStatus() + "/" + hsStatus + ", " + | |
| result.bytesConsumed() + "/" + result.bytesProduced() + | |
| " bytes"); | |
| if (hsStatus == HandshakeStatus.FINISHED) { | |
| log("\t...ready for application data"); | |
| } | |
| } | |
| private static void log(String str) { | |
| if (logging) { | |
| System.out.println(str); | |
| } | |
| } | |
| } |