1 /* 2 * Copyright (c) 2003, 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 /* 25 * @test 26 * @bug 7126889 27 * @summary Incorrect SSLEngine debug output 28 * @library /lib /lib/security 29 * @run main DebugReportsOneExtraByte 30 */ 31 /* 32 * Debug output was reporting n+1 bytes of data was written when it was 33 * really was n. 34 * 35 * SunJSSE does not support dynamic system properties, no way to re-use 36 * system properties in samevm/agentvm mode. 37 */ 38 39 /** 40 * A SSLEngine usage example which simplifies the presentation 41 * 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 them 45 * as directly connected pipes. 46 * 47 * Note, this is a *very* simple example: real code will be much more 48 * involved. For example, different threading and I/O models could be 49 * used, transport mechanisms could close unexpectedly, and so on. 50 * 51 * When this application runs, notice that several messages 52 * (wrap/unwrap) pass before any application data is consumed or 53 * produced. (For more information, please see the SSL/TLS 54 * specifications.) There may several steps for a successful handshake, 55 * so it's typical to see the following series of operations: 56 * 57 * client server message 58 * ====== ====== ======= 59 * wrap() ... ClientHello 60 * ... unwrap() ClientHello 61 * ... wrap() ServerHello/Certificate 62 * unwrap() ... ServerHello/Certificate 63 * wrap() ... ClientKeyExchange 64 * wrap() ... ChangeCipherSpec 65 * wrap() ... Finished 66 * ... unwrap() ClientKeyExchange 67 * ... unwrap() ChangeCipherSpec 68 * ... unwrap() Finished 69 * ... wrap() ChangeCipherSpec 70 * ... wrap() Finished 71 * unwrap() ... ChangeCipherSpec 72 * unwrap() ... Finished 73 */ 74 75 import javax.net.ssl.*; 76 import javax.net.ssl.SSLEngineResult.*; 77 import java.io.*; 78 import java.security.*; 79 import java.nio.*; 80 81 import jdk.test.lib.process.OutputAnalyzer; 82 import jdk.test.lib.process.ProcessTools; 83 84 public class DebugReportsOneExtraByte { 85 86 /* 87 * Enables logging of the SSLEngine operations. 88 */ 89 private static boolean logging = true; 90 91 private SSLContext sslc; 92 93 private SSLEngine clientEngine; // client Engine 94 private ByteBuffer clientOut; // write side of clientEngine 95 private ByteBuffer clientIn; // read side of clientEngine 96 97 private SSLEngine serverEngine; // server Engine 98 private ByteBuffer serverOut; // write side of serverEngine 99 private ByteBuffer serverIn; // read side of serverEngine 100 101 /* 102 * For data transport, this example uses local ByteBuffers. This 103 * isn't really useful, but the purpose of this example is to show 104 * SSLEngine concepts, not how to do network transport. 105 */ 106 private ByteBuffer cTOs; // "reliable" transport client->server 107 private ByteBuffer sTOc; // "reliable" transport server->client 108 109 /* 110 * The following is to set up the keystores. 111 */ 112 private static String pathToStores = "../../../../javax/net/ssl/etc"; 113 private static String keyStoreFile = "keystore"; 114 private static String trustStoreFile = "truststore"; 115 private static String passwd = "passphrase"; 116 117 private static String keyFilename = 118 System.getProperty("test.src", ".") + "/" + pathToStores + 119 "/" + keyStoreFile; 120 private static String trustFilename = 121 System.getProperty("test.src", ".") + "/" + pathToStores + 122 "/" + trustStoreFile; 123 124 /* 125 * Main entry point for this test. 126 */ 127 public static void main(String args[]) throws Exception { 128 129 if (args.length == 0) { 130 OutputAnalyzer output = ProcessTools.executeTestJvm( 131 "-Dtest.src=" + System.getProperty("test.src"), 132 "-Djavax.net.debug=all", "DebugReportsOneExtraByte", "p"); 133 output.shouldContain("WRITE: TLS10 application_data, length = 8"); 134 135 System.out.println("Test Passed."); 136 } else { 137 // Re-enable TLSv1 since test depends on it 138 SecurityUtils.removeFromDisabledTlsAlgs("TLSv1"); 139 140 DebugReportsOneExtraByte test = new DebugReportsOneExtraByte(); 141 test.runTest(); 142 } 143 } 144 145 /* 146 * Create an initialized SSLContext to use for these tests. 147 */ 148 public DebugReportsOneExtraByte() throws Exception { 149 150 KeyStore ks = KeyStore.getInstance("JKS"); 151 KeyStore ts = KeyStore.getInstance("JKS"); 152 153 char[] passphrase = "passphrase".toCharArray(); 154 155 ks.load(new FileInputStream(keyFilename), passphrase); 156 ts.load(new FileInputStream(trustFilename), passphrase); 157 158 KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); 159 kmf.init(ks, passphrase); 160 161 TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); 162 tmf.init(ts); 163 164 SSLContext sslCtx = SSLContext.getInstance("TLSv1"); 165 166 sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); 167 168 sslc = sslCtx; 169 } 170 171 /* 172 * Run the test. 173 * 174 * Sit in a tight loop, both engines calling wrap/unwrap regardless 175 * of whether data is available or not. We do this until both engines 176 * report back they are closed. 177 * 178 * The main loop handles all of the I/O phases of the SSLEngine's 179 * lifetime: 180 * 181 * initial handshaking 182 * application data transfer 183 * engine closing 184 * 185 * One could easily separate these phases into separate 186 * sections of code. 187 */ 188 private void runTest() throws Exception { 189 boolean dataDone = false; 190 191 createSSLEngines(); 192 createBuffers(); 193 194 SSLEngineResult clientResult; // results from client's last operation 195 SSLEngineResult serverResult; // results from server's last operation 196 197 /* 198 * Examining the SSLEngineResults could be much more involved, 199 * and may alter the overall flow of the application. 200 * 201 * For example, if we received a BUFFER_OVERFLOW when trying 202 * to write to the output pipe, we could reallocate a larger 203 * pipe, but instead we wait for the peer to drain it. 204 */ 205 206 /* 207 * Write one byte in first application packet, the rest 208 * will come later. 209 */ 210 serverOut.limit(1); 211 212 while (!isEngineClosed(clientEngine) || 213 !isEngineClosed(serverEngine)) { 214 215 log("================"); 216 217 clientResult = clientEngine.wrap(clientOut, cTOs); 218 log("client wrap: ", clientResult); 219 runDelegatedTasks(clientResult, clientEngine); 220 221 serverResult = serverEngine.wrap(serverOut, sTOc); 222 log("server wrap: ", serverResult); 223 runDelegatedTasks(serverResult, serverEngine); 224 225 // Next wrap will split. 226 if (serverOut.position() == 1) { 227 serverOut.limit(serverOut.capacity()); 228 } 229 230 cTOs.flip(); 231 sTOc.flip(); 232 233 log("----"); 234 235 clientResult = clientEngine.unwrap(sTOc, clientIn); 236 log("client unwrap: ", clientResult); 237 runDelegatedTasks(clientResult, clientEngine); 238 239 serverResult = serverEngine.unwrap(cTOs, serverIn); 240 log("server unwrap: ", serverResult); 241 runDelegatedTasks(serverResult, serverEngine); 242 243 cTOs.compact(); 244 sTOc.compact(); 245 246 /* 247 * After we've transfered all application data between the client 248 * and server, we close the clientEngine's outbound stream. 249 * This generates a close_notify handshake message, which the 250 * server engine receives and responds by closing itself. 251 */ 252 if (!dataDone && (clientOut.limit() == serverIn.position()) && 253 (serverOut.limit() == clientIn.position())) { 254 255 /* 256 * A sanity check to ensure we got what was sent. 257 */ 258 checkTransfer(serverOut, clientIn); 259 checkTransfer(clientOut, serverIn); 260 261 log("\tClosing clientEngine's *OUTBOUND*..."); 262 clientEngine.closeOutbound(); 263 dataDone = true; 264 } 265 } 266 } 267 268 /* 269 * Using the SSLContext created during object creation, 270 * create/configure the SSLEngines we'll use for this test. 271 */ 272 private void createSSLEngines() throws Exception { 273 /* 274 * Configure the serverEngine to act as a server in the SSL/TLS 275 * handshake. Also, require SSL client authentication. 276 */ 277 serverEngine = sslc.createSSLEngine(); 278 serverEngine.setUseClientMode(false); 279 serverEngine.setNeedClientAuth(true); 280 281 // Force a block-oriented ciphersuite. 282 serverEngine.setEnabledCipherSuites( 283 new String [] {"TLS_RSA_WITH_AES_128_CBC_SHA"}); 284 285 /* 286 * Similar to above, but using client mode instead. 287 */ 288 clientEngine = sslc.createSSLEngine("client", 80); 289 clientEngine.setUseClientMode(true); 290 } 291 292 /* 293 * Create and size the buffers appropriately. 294 */ 295 private void createBuffers() { 296 297 /* 298 * We'll assume the buffer sizes are the same 299 * between client and server. 300 */ 301 SSLSession session = clientEngine.getSession(); 302 int appBufferMax = session.getApplicationBufferSize(); 303 int netBufferMax = session.getPacketBufferSize(); 304 305 /* 306 * We'll make the input buffers a bit bigger than the max needed 307 * size, so that unwrap()s following a successful data transfer 308 * won't generate BUFFER_OVERFLOWS. 309 * 310 * We'll use a mix of direct and indirect ByteBuffers for 311 * tutorial purposes only. In reality, only use direct 312 * ByteBuffers when they give a clear performance enhancement. 313 */ 314 clientIn = ByteBuffer.allocate(appBufferMax + 50); 315 serverIn = ByteBuffer.allocate(appBufferMax + 50); 316 317 cTOs = ByteBuffer.allocateDirect(netBufferMax); 318 sTOc = ByteBuffer.allocateDirect(netBufferMax); 319 320 // No need to write anything on the client side, it will 321 // just confuse the output. 322 clientOut = ByteBuffer.wrap("".getBytes()); 323 // 10 bytes long 324 serverOut = ByteBuffer.wrap("Hi Client!".getBytes()); 325 } 326 327 /* 328 * If the result indicates that we have outstanding tasks to do, 329 * go ahead and run them in this thread. 330 */ 331 private static void runDelegatedTasks(SSLEngineResult result, 332 SSLEngine engine) throws Exception { 333 334 if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) { 335 Runnable runnable; 336 while ((runnable = engine.getDelegatedTask()) != null) { 337 log("\trunning delegated task..."); 338 runnable.run(); 339 } 340 HandshakeStatus hsStatus = engine.getHandshakeStatus(); 341 if (hsStatus == HandshakeStatus.NEED_TASK) { 342 throw new Exception( 343 "handshake shouldn't need additional tasks"); 344 } 345 log("\tnew HandshakeStatus: " + hsStatus); 346 } 347 } 348 349 private static boolean isEngineClosed(SSLEngine engine) { 350 return (engine.isOutboundDone() && engine.isInboundDone()); 351 } 352 353 /* 354 * Simple check to make sure everything came across as expected. 355 */ 356 private static void checkTransfer(ByteBuffer a, ByteBuffer b) 357 throws Exception { 358 a.flip(); 359 b.flip(); 360 361 if (!a.equals(b)) { 362 throw new Exception("Data didn't transfer cleanly"); 363 } else { 364 log("\tData transferred cleanly"); 365 } 366 367 a.position(a.limit()); 368 b.position(b.limit()); 369 a.limit(a.capacity()); 370 b.limit(b.capacity()); 371 } 372 373 /* 374 * Logging code 375 */ 376 private static boolean resultOnce = true; 377 378 private static void log(String str, SSLEngineResult result) { 379 if (!logging) { 380 return; 381 } 382 if (resultOnce) { 383 resultOnce = false; 384 System.out.println("The format of the SSLEngineResult is: \n" + 385 "\t\"getStatus() / getHandshakeStatus()\" +\n" + 386 "\t\"bytesConsumed() / bytesProduced()\"\n"); 387 } 388 HandshakeStatus hsStatus = result.getHandshakeStatus(); 389 log(str + 390 result.getStatus() + "/" + hsStatus + ", " + 391 result.bytesConsumed() + "/" + result.bytesProduced() + 392 " bytes"); 393 if (hsStatus == HandshakeStatus.FINISHED) { 394 log("\t...ready for application data"); 395 } 396 } 397 398 private static void log(String str) { 399 if (logging) { 400 System.out.println(str); 401 } 402 } 403 }