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