1 /*
   2  * Copyright (c) 2018, 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 8164879
  27  * @library /lib/testlibrary ../../
  28  * @summary Verify AES/GCM's limits set in the jdk.tls.keyLimits property
  29  * start a new handshake sequence to renegotiate the symmetric key with an
  30  * SSLSocket connection.  This test verifies the handshake method was called
  31  * via debugging info.  It does not verify the renegotiation was successful
  32  * as that is very hard.
  33  *
  34  * @run main SSLEngineKeyLimit 0 server AES/GCM/NoPadding keyupdate 1050000
  35  * @run main SSLEngineKeyLimit 1 client AES/GCM/NoPadding keyupdate 2^22
  36  */
  37 
  38 /*
  39  * This test runs in another process so we can monitor the debug
  40  * results.  The OutputAnalyzer must see correct debug output to return a
  41  * success.
  42  */
  43 
  44 import javax.net.ssl.KeyManagerFactory;
  45 import javax.net.ssl.SSLContext;
  46 import javax.net.ssl.SSLEngine;
  47 import javax.net.ssl.SSLEngineResult;
  48 import javax.net.ssl.TrustManagerFactory;
  49 import java.io.File;
  50 import java.io.PrintWriter;
  51 import java.nio.ByteBuffer;
  52 import java.security.KeyStore;
  53 import java.security.SecureRandom;
  54 import java.util.Arrays;
  55 
  56 import jdk.testlibrary.ProcessTools;
  57 import jdk.testlibrary.Utils;
  58 import jdk.testlibrary.OutputAnalyzer;
  59 
  60 public class SSLEngineKeyLimit {
  61 
  62     SSLEngine eng;
  63     static ByteBuffer cTos;
  64     static ByteBuffer sToc;
  65     static ByteBuffer outdata;
  66     ByteBuffer buf;
  67     static boolean ready = false;
  68 
  69     static String pathToStores = "../../../../javax/net/ssl/etc/";
  70     static String keyStoreFile = "keystore";
  71     static String trustStoreFile = "truststore";
  72     static String passwd = "passphrase";
  73     static String keyFilename;
  74     static int dataLen = 10240;
  75     static boolean serverwrite = true;
  76     int totalDataLen = 0;
  77     static boolean sc = true;
  78     static boolean debug = false;
  79     int delay = 1;
  80 
  81     SSLEngineKeyLimit() {
  82         buf = ByteBuffer.allocate(dataLen*4);
  83     }
  84 
  85     /**
  86      * args should have two values:  server|client, <limit size>
  87      * Prepending 'p' is for internal use only.
  88      */
  89     public static void main(String args[]) throws Exception {
  90 
  91         for (int i = 0; i < args.length; i++) {
  92             System.out.print(" " + args[i]);
  93         }
  94         System.out.println();
  95         if (args[0].compareTo("p") != 0) {
  96             boolean expectedFail = (Integer.parseInt(args[0]) == 1);
  97             if (expectedFail) {
  98                 System.out.println("Test expected to not find updated msg");
  99             }
 100 
 101             // Write security property file to overwrite default
 102             File f = new File("keyusage."+ System.nanoTime());
 103             PrintWriter p = new PrintWriter(f);
 104             p.write("jdk.tls.keyLimits=");
 105             for (int i = 2; i < args.length; i++) {
 106                 p.write(" "+ args[i]);
 107             }
 108             p.close();
 109 
 110             System.setProperty("test.java.opts",
 111                     "-Dtest.src=" + System.getProperty("test.src") +
 112                             " -Dtest.jdk=" + System.getProperty("test.jdk") +
 113                             " -Djavax.net.debug=ssl" +
 114                             " -Djava.security.properties=" + f.getName());
 115 
 116             System.out.println("test.java.opts: " +
 117                     System.getProperty("test.java.opts"));
 118 
 119             ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true,
 120                     Utils.addTestJavaOpts("SSLEngineKeyLimit", "p", args[1]));
 121 
 122             OutputAnalyzer output = ProcessTools.executeProcess(pb);
 123             try {
 124                 if (expectedFail) {
 125                     output.shouldNotContain("KeyUpdate: write key updated");
 126                     output.shouldNotContain("KeyUpdate: read key updated");
 127                 } else {
 128                     output.shouldContain("KeyUpdate: write key updated");
 129                     output.shouldContain("KeyUpdate: read key updated");
 130                 }
 131             } catch (Exception e) {
 132                 throw e;
 133             } finally {
 134                 System.out.println("-- BEGIN Stdout:");
 135                 System.out.println(output.getStdout());
 136                 System.out.println("-- END Stdout");
 137                 System.out.println("-- BEGIN Stderr:");
 138                 System.out.println(output.getStderr());
 139                 System.out.println("-- END Stderr");
 140             }
 141             return;
 142         }
 143 
 144         if (args[0].compareTo("p") != 0) {
 145             throw new Exception ("Tried to run outside of a spawned process");
 146         }
 147 
 148         if (args[1].compareTo("client") == 0) {
 149             serverwrite = false;
 150         }
 151 
 152         cTos = ByteBuffer.allocateDirect(dataLen*4);
 153         keyFilename =
 154             System.getProperty("test.src", "./") + "/" + pathToStores +
 155                 "/" + keyStoreFile;
 156 
 157         System.setProperty("javax.net.ssl.keyStore", keyFilename);
 158         System.setProperty("javax.net.ssl.keyStorePassword", passwd);
 159 
 160         sToc = ByteBuffer.allocateDirect(dataLen*4);
 161         outdata = ByteBuffer.allocateDirect(dataLen);
 162 
 163         byte[] data  = new byte[dataLen];
 164         Arrays.fill(data, (byte)0x0A);
 165         outdata.put(data);
 166         outdata.flip();
 167         cTos.clear();
 168         sToc.clear();
 169 
 170         Thread ts = new Thread(serverwrite ? new Client() : new Server());
 171         ts.start();
 172         (serverwrite ? new Server() : new Client()).run();
 173         ts.interrupt();
 174         ts.join();
 175     }
 176 
 177     private static void doTask(SSLEngineResult result,
 178             SSLEngine engine) throws Exception {
 179 
 180         if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) {
 181             Runnable runnable;
 182             while ((runnable = engine.getDelegatedTask()) != null) {
 183                 System.out.println("\trunning delegated task...");
 184                 runnable.run();
 185             }
 186             SSLEngineResult.HandshakeStatus hsStatus = engine.getHandshakeStatus();
 187             if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK) {
 188                 throw new Exception(
 189                     "handshake shouldn't need additional tasks");
 190             }
 191             print("\tnew HandshakeStatus: " + hsStatus);
 192         }
 193     }
 194 
 195     static void print(String s) {
 196         if (debug) {
 197             System.out.println(s);
 198         }
 199     }
 200 
 201     static void log(String s, SSLEngineResult r) {
 202         if (!debug) {
 203             return;
 204         }
 205         System.out.println(s + ": " +
 206                 r.getStatus() + "/" + r.getHandshakeStatus()+ " " +
 207                 r.bytesConsumed() + "/" + r.bytesProduced() + " ");
 208 
 209     }
 210 
 211     void write() throws Exception {
 212         int i = 0;
 213         SSLEngineResult r;
 214         boolean again = true;
 215 
 216         while (!ready) {
 217             Thread.sleep(delay);
 218         }
 219         print("Write-side. ");
 220 
 221         while (i++ < 120) {
 222             while (sc) {
 223                 Thread.sleep(delay);
 224             }
 225 
 226             outdata.rewind();
 227             print("write wrap");
 228 
 229             while (true) {
 230                 r = eng.wrap(outdata, getWriteBuf());
 231                 log("write wrap", r);
 232                 if (debug && r.getStatus() != SSLEngineResult.Status.OK) {
 233                     System.out.println("outdata pos: " + outdata.position() +
 234                             " rem: " + outdata.remaining() +
 235                             " lim: " + outdata.limit() +
 236                             " cap: " + outdata.capacity());
 237                     System.out.println("writebuf pos: " + getWriteBuf().position() +
 238                             " rem: " + getWriteBuf().remaining() +
 239                             " lim: " + getWriteBuf().limit() +
 240                             " cap: " + getWriteBuf().capacity());
 241                 }
 242                 if (again && r.getStatus() == SSLEngineResult.Status.OK &&
 243                         r.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
 244                     print("again");
 245                     again = false;
 246                     continue;
 247                 }
 248                 break;
 249             }
 250             doTask(r, eng);
 251             getWriteBuf().flip();
 252             sc = true;
 253             while (sc) {
 254                 Thread.sleep(delay);
 255             }
 256 
 257             while (true) {
 258                 buf.clear();
 259                 r = eng.unwrap(getReadBuf(), buf);
 260                 log("write unwrap", r);
 261                 if (debug && r.getStatus() != SSLEngineResult.Status.OK) {
 262                     System.out.println("buf pos: " + buf.position() +
 263                             " rem: " + buf.remaining() +
 264                             " lim: " + buf.limit() +
 265                             " cap: " + buf.capacity());
 266                     System.out.println("readbuf pos: " + getReadBuf().position() +
 267                             " rem: " + getReadBuf().remaining() +
 268                             " lim: " + getReadBuf().limit() +
 269                             " cap:"  + getReadBuf().capacity());
 270                 }
 271                 break;
 272             }
 273             doTask(r, eng);
 274             getReadBuf().compact();
 275             print("compacted readbuf pos: " + getReadBuf().position() +
 276                     " rem: " + getReadBuf().remaining() +
 277                     " lim: " + getReadBuf().limit() +
 278                     " cap: " + getReadBuf().capacity());
 279             sc = true;
 280         }
 281     }
 282 
 283     void read() throws Exception {
 284         byte b = 0x0B;
 285         ByteBuffer buf2 = ByteBuffer.allocateDirect(dataLen);
 286         SSLEngineResult r = null;
 287         boolean exit, again = true;
 288 
 289         while (eng == null) {
 290             Thread.sleep(delay);
 291         }
 292 
 293         try {
 294             System.out.println("connected");
 295             print("entering read loop");
 296             ready = true;
 297             while (true) {
 298 
 299                 while (!sc) {
 300                     Thread.sleep(delay);
 301                 }
 302 
 303                 print("read wrap");
 304                 exit = false;
 305                 while (!exit) {
 306                     buf2.put(b);
 307                     buf2.flip();
 308                     r = eng.wrap(buf2, getWriteBuf());
 309                     log("read wrap", r);
 310                     if (debug ) { //&& r.getStatus() != SSLEngineResult.Status.OK) {
 311                         System.out.println("buf2 pos: " + buf2.position() +
 312                                 " rem: " + buf2.remaining() +
 313                                 " cap: " + buf2.capacity());
 314                         System.out.println("writebuf pos: " + getWriteBuf().position() +
 315                                 " rem: " + getWriteBuf().remaining() +
 316                                 " cap: " + getWriteBuf().capacity());
 317                     }
 318                     if (again && r.getStatus() == SSLEngineResult.Status.OK &&
 319                             r.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
 320                         buf2.compact();
 321                         again = false;
 322                         continue;
 323                     }
 324                     exit = true;
 325                 }
 326                 doTask(r, eng);
 327                 buf2.clear();
 328                 getWriteBuf().flip();
 329 
 330                 sc = false;
 331 
 332                 while (!sc) {
 333                     Thread.sleep(delay);
 334                 }
 335 
 336                 while (true) {
 337                         buf.clear();
 338                         r = eng.unwrap(getReadBuf(), buf);
 339                         log("read unwrap", r);
 340                         if (debug && r.getStatus() != SSLEngineResult.Status.OK) {
 341                             System.out.println("buf pos " + buf.position() +
 342                                     " rem: " + buf.remaining() +
 343                                     " lim: " + buf.limit() +
 344                                     " cap: " + buf.capacity());
 345                             System.out.println("readbuf pos: " + getReadBuf().position() +
 346                                     " rem: " + getReadBuf().remaining() +
 347                                     " lim: " + getReadBuf().limit() +
 348                                     " cap: " + getReadBuf().capacity());
 349                             doTask(r, eng);
 350                         }
 351 
 352                     if (again && r.getStatus() == SSLEngineResult.Status.OK &&
 353                             r.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
 354                         buf.clear();
 355                         print("again");
 356                         again = false;
 357                         continue;
 358 
 359                     }
 360                     break;
 361                 }
 362                 buf.clear();
 363                 getReadBuf().compact();
 364 
 365                 totalDataLen += r.bytesProduced();
 366                 sc = false;
 367             }
 368         } catch (Exception e) {
 369             System.out.println(e.getMessage());
 370             e.printStackTrace();
 371         }
 372         print("TotalDataLen = " + totalDataLen);
 373     }
 374 
 375     ByteBuffer getReadBuf() {
 376         return null;
 377     }
 378 
 379     ByteBuffer getWriteBuf() {
 380         return null;
 381     }
 382 
 383 
 384     SSLContext initContext() throws Exception {
 385         SSLContext sc = SSLContext.getInstance("TLSv1.3");
 386         KeyStore ks = KeyStore.getInstance(
 387                 new File(System.getProperty("javax.net.ssl.keyStore")),
 388                 passwd.toCharArray());
 389         KeyManagerFactory kmf =
 390                 KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
 391         kmf.init(ks, passwd.toCharArray());
 392         TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
 393         tmf.init(ks);
 394         sc.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
 395         return sc;
 396     }
 397 
 398     static class Server extends SSLEngineKeyLimit implements Runnable {
 399         Server() throws Exception {
 400             super();
 401             eng = initContext().createSSLEngine();
 402             eng.setUseClientMode(false);
 403             eng.setNeedClientAuth(true);
 404         }
 405         
 406         public void run() {
 407             try {
 408                 if (serverwrite) {
 409                     write();
 410                 } else {
 411                     read();
 412                 }
 413 
 414             } catch (Exception e) {
 415                 System.out.println("server: " + e.getMessage());
 416                 e.printStackTrace();
 417             }
 418             System.out.println("Server closed");
 419         }
 420 
 421         @Override
 422         ByteBuffer getWriteBuf() {
 423             return sToc;
 424         }
 425         @Override
 426         ByteBuffer getReadBuf() {
 427             return cTos;
 428         }
 429     }
 430 
 431 
 432     static class Client extends SSLEngineKeyLimit implements Runnable {
 433         Client() throws Exception {
 434             super();
 435             eng = initContext().createSSLEngine("client", 80);
 436             eng.setUseClientMode(true);
 437         }
 438 
 439         public void run() {
 440             try {
 441                 if (!serverwrite) {
 442                     write();
 443                 } else {
 444                     read();
 445                 }
 446             } catch (Exception e) {
 447                 System.out.println("client: " + e.getMessage());
 448                 e.printStackTrace();
 449             }
 450         }
 451         @Override
 452         ByteBuffer getWriteBuf() {
 453             return cTos;
 454         }
 455         @Override
 456         ByteBuffer getReadBuf() {
 457             return sToc;
 458         }
 459     }
 460 }