1 /*
   2  * Copyright (c) 2004, 2007, 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 5019096
  27  * @summary Add scatter/gather APIs for SSLEngine
  28  * @run main/othervm Arrays SSL
  29  * @run main/othervm Arrays TLS
  30  * @run main/othervm Arrays SSLv3
  31  * @run main/othervm Arrays TLSv1
  32  * @run main/othervm Arrays TLSv1.1
  33  * @run main/othervm Arrays TLSv1.2
  34  * @run main/othervm Arrays TLSv1.3
  35  * @run main/othervm -Djdk.tls.acknowledgeCloseNotify=true Arrays TLSv1.3
  36  */
  37 
  38 import javax.net.ssl.*;
  39 import javax.net.ssl.SSLEngineResult.*;
  40 import java.io.*;
  41 import java.security.*;
  42 import java.nio.*;
  43 
  44 public class Arrays {
  45 
  46     private static boolean debug = false;
  47     private static boolean acknowledgeCloseNotify =
  48         "true".equals(System.getProperty("jdk.tls.acknowledgeCloseNotify"));
  49 
  50     private SSLContext sslc;
  51     private SSLEngine ssle1;    // client
  52     private SSLEngine ssle2;    // server
  53 
  54     private static String pathToStores = "../etc";
  55     private static String keyStoreFile = "keystore";
  56     private static String trustStoreFile = "truststore";
  57     private static String passwd = "passphrase";
  58 
  59     private static String keyFilename =
  60             System.getProperty("test.src", "./") + "/" + pathToStores +
  61                 "/" + keyStoreFile;
  62     private static String trustFilename =
  63             System.getProperty("test.src", "./") + "/" + pathToStores +
  64                 "/" + trustStoreFile;
  65 
  66     private ByteBuffer [] appOutArray1;
  67     private ByteBuffer [] appInArray1;
  68 
  69     private ByteBuffer appOut2;         // write side of ssle2
  70     private ByteBuffer appIn2;          // read side of ssle2
  71 
  72     private ByteBuffer oneToTwo;        // "reliable" transport ssle1->ssle2
  73     private ByteBuffer twoToOne;        // "reliable" transport ssle2->ssle1
  74 
  75     /*
  76      * Majority of the test case is here, setup is done below.
  77      */
  78     private void createSSLEngines() throws Exception {
  79         ssle1 = sslc.createSSLEngine("client", 1);
  80         ssle1.setUseClientMode(true);
  81 
  82         ssle2 = sslc.createSSLEngine();
  83         ssle2.setUseClientMode(false);
  84         ssle2.setNeedClientAuth(true);
  85     }
  86 
  87     private void runTest() throws Exception {
  88         boolean dataDone = false;
  89 
  90         createSSLEngines();
  91         createBuffers();
  92 
  93         SSLEngineResult result1;        // ssle1's results from last operation
  94         SSLEngineResult result2;        // ssle2's results from last operation
  95 
  96         while (!isEngineClosed(ssle1) || !isEngineClosed(ssle2)) {
  97 
  98             log("================");
  99 
 100             result1 = ssle1.wrap(appOutArray1, oneToTwo);
 101             result2 = ssle2.wrap(appOut2, twoToOne);
 102 
 103             log("wrap1:  " + result1);
 104             log("oneToTwo  = " + oneToTwo);
 105             log("");
 106 
 107             log("wrap2:  " + result2);
 108             log("twoToOne  = " + twoToOne);
 109 
 110             runDelegatedTasks(result1, ssle1);
 111             runDelegatedTasks(result2, ssle2);
 112 
 113             oneToTwo.flip();
 114             twoToOne.flip();
 115 
 116             log("----");
 117 
 118             result1 = ssle1.unwrap(twoToOne, appInArray1);
 119             result2 = ssle2.unwrap(oneToTwo, appIn2);
 120 
 121             log("unwrap1: " + result1);
 122             log("twoToOne  = " + twoToOne);
 123             log("");
 124 
 125             log("unwrap2: " + result2);
 126             log("oneToTwo  = " + oneToTwo);
 127 
 128             runDelegatedTasks(result1, ssle1);
 129             runDelegatedTasks(result2, ssle2);
 130 
 131             oneToTwo.compact();
 132             twoToOne.compact();
 133 
 134             /*
 135              * If we've transfered all the data between app1 and app2,
 136              * we try to close and see what that gets us.
 137              */
 138             if (!dataDone) {
 139                 boolean done = true;
 140 
 141                 for (int i = 0; i < appOutArray1.length; i++) {
 142                     if (appOutArray1[i].remaining() != 0) {
 143                         log("1st out not done");
 144                         done = false;
 145                     }
 146                 }
 147 
 148                 if (appOut2.remaining() != 0) {
 149                     log("2nd out not done");
 150                     done = false;
 151                 }
 152 
 153                 if (done) {
 154                     log("Closing ssle1's *OUTBOUND*...");
 155                     for (int i = 0; i < appOutArray1.length; i++) {
 156                         appOutArray1[i].rewind();
 157                     }
 158                     ssle1.closeOutbound();
 159                     String protocol = ssle2.getSession().getProtocol();
 160                     if (!acknowledgeCloseNotify) {
 161                         switch (ssle2.getSession().getProtocol()) {
 162                             case "SSLv3":
 163                             case "TLSv1":
 164                             case "TLSv1.1":
 165                             case "TLSv1.2":
 166                                 break;
 167                             default:    // TLSv1.3
 168                                 // TLS 1.3, half-close only.
 169                                 ssle2.closeOutbound();
 170                         }
 171                     }
 172                     dataDone = true;
 173                 }
 174             }
 175         }
 176         checkTransfer(appOutArray1,  appIn2);
 177         appInArray1[appInArray1.length - 1].limit(
 178             appInArray1[appInArray1.length - 1].position());
 179         checkTransfer(appInArray1, appOut2);
 180     }
 181 
 182     private static String contextVersion;
 183     public static void main(String args[]) throws Exception {
 184         contextVersion = args[0];
 185 
 186         Arrays test;
 187 
 188         test = new Arrays();
 189 
 190         test.createSSLEngines();
 191 
 192         test.runTest();
 193 
 194         System.err.println("Test Passed.");
 195     }
 196 
 197     /*
 198      * **********************************************************
 199      * Majority of the test case is above, below is just setup stuff
 200      * **********************************************************
 201      */
 202 
 203     public Arrays() throws Exception {
 204         sslc = getSSLContext(keyFilename, trustFilename);
 205     }
 206 
 207     /*
 208      * Create an initialized SSLContext to use for this test.
 209      */
 210     private SSLContext getSSLContext(String keyFile, String trustFile)
 211             throws Exception {
 212 
 213         KeyStore ks = KeyStore.getInstance("JKS");
 214         KeyStore ts = KeyStore.getInstance("JKS");
 215 
 216         char[] passphrase = "passphrase".toCharArray();
 217 
 218         ks.load(new FileInputStream(keyFile), passphrase);
 219         ts.load(new FileInputStream(trustFile), passphrase);
 220 
 221         KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
 222         kmf.init(ks, passphrase);
 223 
 224         TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
 225         tmf.init(ts);
 226 
 227         SSLContext sslCtx = SSLContext.getInstance(contextVersion);
 228 
 229         sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
 230 
 231         return sslCtx;
 232     }
 233 
 234     private void createBuffers() {
 235         // Size the buffers as appropriate.
 236 
 237         SSLSession session = ssle1.getSession();
 238         int appBufferMax = session.getApplicationBufferSize();
 239         int netBufferMax = session.getPacketBufferSize();
 240 
 241         appIn2 = ByteBuffer.allocateDirect(appBufferMax + 50);
 242 
 243         oneToTwo = ByteBuffer.allocateDirect(netBufferMax);
 244         twoToOne = ByteBuffer.allocateDirect(netBufferMax);
 245 
 246         ByteBuffer strBB = ByteBuffer.wrap(
 247             "Hi Engine2, I'm SSLEngine1, So Be it" .getBytes());
 248 
 249         strBB.position(0);
 250         strBB.limit(5);
 251         ByteBuffer appOut1a = strBB.slice();
 252 
 253         strBB.position(5);
 254         strBB.limit(15);
 255         ByteBuffer appOut1b = strBB.slice();
 256 
 257         strBB.position(15);
 258         strBB.limit(strBB.capacity());
 259         ByteBuffer appOut1c = strBB.slice();
 260 
 261         strBB.rewind();
 262 
 263         appOutArray1 = new ByteBuffer [] { appOut1a, appOut1b, appOut1c };
 264 
 265         appOut2 = ByteBuffer.wrap("Hello Engine1, I'm SSLEngine2".getBytes());
 266 
 267         ByteBuffer appIn1a = ByteBuffer.allocateDirect(5);
 268         ByteBuffer appIn1b = ByteBuffer.allocateDirect(10);
 269         ByteBuffer appIn1c = ByteBuffer.allocateDirect(appBufferMax + 50);
 270         appInArray1 = new ByteBuffer [] { appIn1a, appIn1b, appIn1c };
 271 
 272         log("AppOut1a = " + appOut1a);
 273         log("AppOut1a = " + appOut1b);
 274         log("AppOut1a = " + appOut1c);
 275         log("AppOut2 = " + appOut2);
 276         log("");
 277     }
 278 
 279     private static void runDelegatedTasks(SSLEngineResult result,
 280             SSLEngine engine) throws Exception {
 281 
 282         if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
 283             Runnable runnable;
 284             while ((runnable = engine.getDelegatedTask()) != null) {
 285                 log("running delegated task...");
 286                 runnable.run();
 287             }
 288         }
 289     }
 290 
 291     private static boolean isEngineClosed(SSLEngine engine) {
 292         return (engine.isOutboundDone() && engine.isInboundDone());
 293     }
 294 
 295     private static void checkTransfer(ByteBuffer [] a, ByteBuffer b)
 296             throws Exception {
 297 
 298         b.flip();
 299 
 300         for (int i = 0; i < a.length; i++) {
 301             a[i].rewind();
 302 
 303             b.limit(b.position() + a[i].remaining());
 304 
 305             if (!a[i].equals(b)) {
 306                 throw new Exception("Data didn't transfer cleanly");
 307             }
 308 
 309             b.position(b.limit());
 310         }
 311 
 312         log("Data transferred cleanly");
 313     }
 314 
 315     private static void log(String str) {
 316         if (debug) {
 317             System.err.println(str);
 318         }
 319     }
 320 }