1 /*
   2  * Copyright (c) 2015, 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 import javax.net.ssl.KeyManagerFactory;
  25 import javax.net.ssl.SNIHostName;
  26 import javax.net.ssl.SNIMatcher;
  27 import javax.net.ssl.SNIServerName;
  28 import javax.net.ssl.SSLContext;
  29 import javax.net.ssl.SSLEngine;
  30 import javax.net.ssl.SSLEngineResult;
  31 import javax.net.ssl.SSLException;
  32 import javax.net.ssl.SSLParameters;
  33 import javax.net.ssl.TrustManagerFactory;
  34 import java.io.File;
  35 import java.io.FileInputStream;
  36 import java.io.IOException;
  37 import java.nio.ByteBuffer;
  38 import java.security.KeyManagementException;
  39 import java.security.KeyStore;
  40 import java.security.KeyStoreException;
  41 import java.security.NoSuchAlgorithmException;
  42 import java.security.UnrecoverableKeyException;
  43 import java.security.cert.CertificateException;
  44 import java.util.ArrayList;
  45 import java.util.Arrays;
  46 import java.util.HashMap;
  47 import java.util.LinkedList;
  48 import java.util.List;
  49 import java.util.Map;
  50 
  51 /**
  52  * Basic class to inherit SSLEngine test cases from it. Tests apply for
  53  * the TLS or DTLS security protocols and their versions.
  54  */
  55 abstract public class SSLEngineTestCase {
  56 
  57     public enum Ciphers {
  58 
  59         /**
  60          * Ciphers supported by the tested SSLEngine without those with kerberos
  61          * authentication.
  62          */
  63         SUPPORTED_NON_KRB_CIPHERS(SSLEngineTestCase.SUPPORTED_NON_KRB_CIPHERS,
  64                 "Supported non kerberos"),
  65         /**
  66          * Ciphers supported by the tested SSLEngine without those with kerberos
  67          * authentication and without those with SHA256 ans SHA384.
  68          */
  69         SUPPORTED_NON_KRB_NON_SHA_CIPHERS(SSLEngineTestCase.SUPPORTED_NON_KRB_NON_SHA_CIPHERS,
  70                 "Supported non kerberos non SHA256 and SHA384"),
  71         /**
  72          * Ciphers supported by the tested SSLEngine with kerberos authentication.
  73          */
  74         SUPPORTED_KRB_CIPHERS(SSLEngineTestCase.SUPPORTED_KRB_CIPHERS,
  75                 "Supported kerberos"),
  76         /**
  77          * Ciphers enabled by default for the tested SSLEngine without kerberos
  78          * and anon.
  79          */
  80         ENABLED_NON_KRB_NOT_ANON_CIPHERS(
  81                 SSLEngineTestCase.ENABLED_NON_KRB_NOT_ANON_CIPHERS,
  82                 "Enabled by default non kerberos not anonymous"),
  83         /**
  84          * Ciphers unsupported by the tested SSLEngine.
  85          */
  86         UNSUPPORTED_CIPHERS(SSLEngineTestCase.UNSUPPORTED_CIPHERS,
  87                 "Unsupported");
  88 
  89         Ciphers(String[] ciphers, String description) {
  90             this.ciphers = ciphers;
  91             this.description = description;
  92         }
  93 
  94         final String[] ciphers;
  95         final String description;
  96     }
  97 
  98     /**
  99      * Enumeration used to distinguish handshake mode in
 100      * {@link SSLEngineTestCase#doHandshake(javax.net.ssl.SSLEngine,
 101      * javax.net.ssl.SSLEngine, int, SSLEngineTestCase.HandshakeMode, boolean)
 102      * SSLEngineTestCase.doHandshake} method.
 103      */
 104     public enum HandshakeMode {
 105 
 106         /**
 107          * Initial handshake done for the first time: both engines call
 108          * {@link SSLEngine#beginHandshake()} method.
 109          */
 110         INITIAL_HANDSHAKE,
 111         /**
 112          * Repeated handshake done by client: client engine calls
 113          * {@link SSLEngine#beginHandshake()} method.
 114          */
 115         REHANDSHAKE_BEGIN_CLIENT,
 116         /**
 117          * Repeated handshake done by server: server engine calls
 118          * {@link SSLEngine#beginHandshake()} method.
 119          */
 120         REHANDSHAKE_BEGIN_SERVER;
 121     }
 122     /**
 123      * Security protocol to be tested: "TLS" or "DTLS" or their versions,
 124      * e.g. "TLSv1", "TLSv1.1", "TLSv1.2", "DTLSv1.0", "DTLSv1.2".
 125      */
 126     public static final String TESTED_SECURITY_PROTOCOL
 127             = System.getProperty("test.security.protocol", "TLS");
 128     /**
 129      * Test mode: "norm", "norm_sni" or "krb".
 130      * Modes "norm" and "norm_sni" are used to run
 131      * with all supported non-kerberos ciphers.
 132      * Mode "krb" is used to run with kerberos ciphers.
 133      */
 134     public static final String TEST_MODE
 135             = System.getProperty("test.mode", "norm");
 136 
 137     private static final String FS = System.getProperty("file.separator", "/");
 138     private static final String PATH_TO_STORES = ".." + FS + "etc";
 139     private static final String KEY_STORE_FILE = "keystore";
 140     private static final String TRUST_STORE_FILE = "truststore";
 141     private static final String PASSWD = "passphrase";
 142 
 143     private static final String KEY_FILE_NAME
 144             = System.getProperty("test.src", ".") + FS + PATH_TO_STORES
 145             + FS + KEY_STORE_FILE;
 146     private static final String TRUST_FILE_NAME
 147             = System.getProperty("test.src", ".") + FS + PATH_TO_STORES
 148             + FS + TRUST_STORE_FILE;
 149 
 150     private static ByteBuffer net;
 151     private static ByteBuffer netReplicatedClient;
 152     private static ByteBuffer netReplicatedServer;
 153     private static final int MAX_HANDSHAKE_LOOPS = 100;
 154     private static final String EXCHANGE_MSG_SENT = "Hello, peer!";
 155     private static boolean doUnwrapForNotHandshakingStatus;
 156     private static boolean endHandshakeLoop = false;
 157     private static final String TEST_SRC = System.getProperty("test.src", ".");
 158     private static final String KTAB_FILENAME = "krb5.keytab.data";
 159     private static final String KRB_REALM = "TEST.REALM";
 160     private static final String KRBTGT_PRINCIPAL = "krbtgt/" + KRB_REALM;
 161     private static final String KRB_USER = "USER";
 162     private static final String KRB_USER_PASSWORD = "password";
 163     private static final String KRB_USER_PRINCIPAL = KRB_USER + "@" + KRB_REALM;
 164     private static final String KRB5_CONF_FILENAME = "krb5.conf";
 165     private static final String PATH_TO_COMMON = ".." + FS + "TLSCommon";
 166     private static final String JAAS_CONF_FILE = PATH_TO_COMMON
 167             + FS + "jaas.conf";
 168     private static final int DELAY = 1000;
 169     private static final String HOST = "localhost";
 170     private static final String SERVER_NAME = "service.localhost";
 171     private static final String SNI_PATTERN = ".*";
 172 
 173     private static final String[] SUPPORTED_NON_KRB_CIPHERS;
 174 
 175     static {
 176         try {
 177             String[] allSupportedCiphers = getContext()
 178                     .createSSLEngine().getSupportedCipherSuites();
 179             List<String> supportedCiphersList = new LinkedList<>();
 180             for (String cipher : allSupportedCiphers) {
 181                 if (!cipher.contains("KRB5")
 182                         && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
 183                     supportedCiphersList.add(cipher);
 184                 }
 185             }
 186             SUPPORTED_NON_KRB_CIPHERS = supportedCiphersList.toArray(new String[0]);
 187         } catch (Exception ex) {
 188             throw new Error("Unexpected issue", ex);
 189         }
 190     }
 191 
 192     private static final String[] SUPPORTED_NON_KRB_NON_SHA_CIPHERS;
 193 
 194     static {
 195         try {
 196             String[] allSupportedCiphers = getContext()
 197                     .createSSLEngine().getSupportedCipherSuites();
 198             List<String> supportedCiphersList = new LinkedList<>();
 199             for (String cipher : allSupportedCiphers) {
 200                 if (!cipher.contains("KRB5")
 201                         && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")
 202                         && !cipher.endsWith("_SHA256")
 203                         && !cipher.endsWith("_SHA384")) {
 204                     supportedCiphersList.add(cipher);
 205                 }
 206             }
 207             SUPPORTED_NON_KRB_NON_SHA_CIPHERS
 208                     = supportedCiphersList.toArray(new String[0]);
 209         } catch (Exception ex) {
 210             throw new Error("Unexpected issue", ex);
 211         }
 212     }
 213 
 214     private static final String[] SUPPORTED_KRB_CIPHERS;
 215 
 216     static {
 217         try {
 218             String[] allSupportedCiphers = getContext()
 219                     .createSSLEngine().getSupportedCipherSuites();
 220             List<String> supportedCiphersList = new LinkedList<>();
 221             for (String cipher : allSupportedCiphers) {
 222                 if (cipher.contains("KRB5")
 223                         && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
 224                     supportedCiphersList.add(cipher);
 225                 }
 226             }
 227             SUPPORTED_KRB_CIPHERS = supportedCiphersList.toArray(new String[0]);
 228         } catch (Exception ex) {
 229             throw new Error("Unexpected issue", ex);
 230         }
 231     }
 232 
 233     private static final String[] ENABLED_NON_KRB_NOT_ANON_CIPHERS;
 234 
 235     static {
 236         try {
 237             SSLEngine temporary = getContext().createSSLEngine();
 238             temporary.setUseClientMode(true);
 239             String[] enabledCiphers = temporary.getEnabledCipherSuites();
 240             List<String> enabledCiphersList = new LinkedList<>();
 241             for (String cipher : enabledCiphers) {
 242                 if (!cipher.contains("anon") && !cipher.contains("KRB5")
 243                         && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
 244                     enabledCiphersList.add(cipher);
 245                 }
 246             }
 247             ENABLED_NON_KRB_NOT_ANON_CIPHERS = enabledCiphersList.toArray(new String[0]);
 248         } catch (Exception ex) {
 249             throw new Error("Unexpected issue", ex);
 250         }
 251     }
 252 
 253     private static final String[] UNSUPPORTED_CIPHERS = {
 254             "SSL_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA",
 255             "SSL_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA",
 256             "SSL_DHE_DSS_WITH_RC4_128_SHA",
 257             "SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA",
 258             "SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA",
 259             "SSL_DH_DSS_WITH_DES_CBC_SHA",
 260             "SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA",
 261             "SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA",
 262             "SSL_DH_RSA_WITH_DES_CBC_SHA",
 263             "SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA",
 264             "SSL_FORTEZZA_DMS_WITH_NULL_SHA",
 265             "SSL_RSA_EXPORT1024_WITH_DES_CBC_SHA",
 266             "SSL_RSA_EXPORT1024_WITH_RC4_56_SHA",
 267             "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5",
 268             "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA",
 269             "SSL_RSA_FIPS_WITH_DES_CBC_SHA",
 270             "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5",
 271             "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA",
 272             "TLS_KRB5_WITH_IDEA_CBC_MD5",
 273             "TLS_KRB5_WITH_IDEA_CBC_SHA",
 274             "SSL_RSA_WITH_IDEA_CBC_SHA",
 275             "TLS_DH_RSA_WITH_AES_128_GCM_SHA256",
 276             "TLS_DH_RSA_WITH_AES_256_GCM_SHA384",
 277             "TLS_DH_DSS_WITH_AES_128_GCM_SHA256",
 278             "TLS_DH_DSS_WITH_AES_256_GCM_SHA384"
 279     };
 280 
 281     private final int maxPacketSize;
 282 
 283     /**
 284      * Constructs test case with the given MFLN maxMacketSize.
 285      *
 286      * @param maxPacketSize - MLFN extension max packet size.
 287      */
 288     public SSLEngineTestCase(int maxPacketSize) {
 289         this.maxPacketSize = maxPacketSize;
 290     }
 291 
 292     /**
 293      * Constructs test case with {@code maxPacketSize = 0}.
 294      */
 295     public SSLEngineTestCase() {
 296         this.maxPacketSize = 0;
 297     }
 298 
 299     /**
 300      * Wraps data with the specified engine.
 301      *
 302      * @param engine        - SSLEngine that wraps data.
 303      * @param wrapper       - Set wrapper id, e.g. "server" of "client". Used for
 304      *                      logging only.
 305      * @param maxPacketSize - Max packet size to check that MFLN extension works
 306      *                      or zero for no check.
 307      * @param app           - Buffer with data to wrap.
 308      * @return - Buffer with wrapped data.
 309      * @throws SSLException - thrown on engine errors.
 310      */
 311     public static ByteBuffer doWrap(SSLEngine engine, String wrapper,
 312                                     int maxPacketSize, ByteBuffer app)
 313             throws SSLException {
 314         return doWrap(engine, wrapper, maxPacketSize,
 315                 app, SSLEngineResult.Status.OK, null);
 316     }
 317 
 318     /**
 319      * Wraps data with the specified engine.
 320      *
 321      * @param engine        - SSLEngine that wraps data.
 322      * @param wrapper       - Set wrapper id, e.g. "server" of "client". Used for
 323      *                      logging only.
 324      * @param maxPacketSize - Max packet size to check that MFLN extension works
 325      *                      or zero for no check.
 326      * @param app           - Buffer with data to wrap.
 327      * @param result        - Array which first element will be used to output wrap
 328      *                      result object.
 329      * @return - Buffer with wrapped data.
 330      * @throws SSLException - thrown on engine errors.
 331      */
 332     public static ByteBuffer doWrap(SSLEngine engine, String wrapper,
 333                                     int maxPacketSize, ByteBuffer app,
 334                                     SSLEngineResult[] result)
 335             throws SSLException {
 336         return doWrap(engine, wrapper, maxPacketSize,
 337                 app, SSLEngineResult.Status.OK, result);
 338     }
 339 
 340     /**
 341      * Wraps data with the specified engine.
 342      *
 343      * @param engine        - SSLEngine that wraps data.
 344      * @param wrapper       - Set wrapper id, e.g. "server" of "client". Used for
 345      *                      logging only.
 346      * @param maxPacketSize - Max packet size to check that MFLN extension works
 347      *                      or zero for no check.
 348      * @param app           - Buffer with data to wrap.
 349      * @param wantedStatus  - Specifies expected result status of wrapping.
 350      * @return - Buffer with wrapped data.
 351      * @throws SSLException - thrown on engine errors.
 352      */
 353     public static ByteBuffer doWrap(SSLEngine engine, String wrapper,
 354                                     int maxPacketSize, ByteBuffer app,
 355                                     SSLEngineResult.Status wantedStatus)
 356             throws SSLException {
 357         return doWrap(engine, wrapper, maxPacketSize,
 358                 app, wantedStatus, null);
 359     }
 360 
 361     /**
 362      * Wraps data with the specified engine.
 363      *
 364      * @param engine        - SSLEngine that wraps data.
 365      * @param wrapper       - Set wrapper id, e.g. "server" of "client". Used for
 366      *                      logging only.
 367      * @param maxPacketSize - Max packet size to check that MFLN extension works
 368      *                      or zero for no check.
 369      * @param app           - Buffer with data to wrap.
 370      * @param wantedStatus  - Specifies expected result status of wrapping.
 371      * @param result        - Array which first element will be used to output wrap
 372      *                      result object.
 373      * @return - Buffer with wrapped data.
 374      * @throws SSLException - thrown on engine errors.
 375      */
 376     public static ByteBuffer doWrap(SSLEngine engine, String wrapper,
 377                                     int maxPacketSize, ByteBuffer app,
 378                                     SSLEngineResult.Status wantedStatus,
 379                                     SSLEngineResult[] result)
 380             throws SSLException {
 381         ByteBuffer net = ByteBuffer.allocate(engine.getSession()
 382                 .getPacketBufferSize());
 383         SSLEngineResult r = engine.wrap(app, net);
 384         net.flip();
 385         int length = net.remaining();
 386         System.out.println(wrapper + " wrapped " + length + " bytes.");
 387         System.out.println(wrapper + " handshake status is "
 388                 + engine.getHandshakeStatus());
 389         if (maxPacketSize < length && maxPacketSize != 0) {
 390             throw new AssertionError("Handshake wrapped net buffer length "
 391                     + length + " exceeds maximum packet size "
 392                     + maxPacketSize);
 393         }
 394         checkResult(r, wantedStatus);
 395         if (result != null && result.length > 0) {
 396             result[0] = r;
 397         }
 398         return net;
 399     }
 400 
 401     /**
 402      * Unwraps data with the specified engine.
 403      *
 404      * @param engine    - SSLEngine that unwraps data.
 405      * @param unwrapper - Set unwrapper id, e.g. "server" of "client". Used for
 406      *                  logging only.
 407      * @param net       - Buffer with data to unwrap.
 408      * @return - Buffer with unwrapped data.
 409      * @throws SSLException - thrown on engine errors.
 410      */
 411     public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper,
 412                                       ByteBuffer net)
 413             throws SSLException {
 414         return doUnWrap(engine, unwrapper, net, SSLEngineResult.Status.OK, null);
 415     }
 416 
 417     /**
 418      * Unwraps data with the specified engine.
 419      *
 420      * @param engine    - SSLEngine that unwraps data.
 421      * @param unwrapper - Set unwrapper id, e.g. "server" of "client". Used for
 422      *                  logging only.
 423      * @param net       - Buffer with data to unwrap.
 424      * @param result    - Array which first element will be used to output wrap
 425      *                  result object.
 426      * @return - Buffer with unwrapped data.
 427      * @throws SSLException - thrown on engine errors.
 428      */
 429     public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper,
 430                                       ByteBuffer net, SSLEngineResult[] result)
 431             throws SSLException {
 432         return doUnWrap(engine, unwrapper, net, SSLEngineResult.Status.OK, result);
 433     }
 434 
 435     /**
 436      * Unwraps data with the specified engine.
 437      *
 438      * @param engine       - SSLEngine that unwraps data.
 439      * @param unwrapper    - Set unwrapper id, e.g. "server" of "client". Used for
 440      *                     logging only.
 441      * @param net          - Buffer with data to unwrap.
 442      * @param wantedStatus - Specifies expected result status of wrapping.
 443      * @return - Buffer with unwrapped data.
 444      * @throws SSLException - thrown on engine errors.
 445      */
 446     public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper,
 447                                       ByteBuffer net,
 448                                       SSLEngineResult.Status wantedStatus)
 449             throws SSLException {
 450         return doUnWrap(engine, unwrapper, net, wantedStatus, null);
 451     }
 452 
 453     /**
 454      * Unwraps data with the specified engine.
 455      *
 456      * @param engine       - SSLEngine that unwraps data.
 457      * @param unwrapper    - Set unwrapper id, e.g. "server" of "client". Used for
 458      *                     logging only.
 459      * @param net          - Buffer with data to unwrap.
 460      * @param wantedStatus - Specifies expected result status of wrapping.
 461      * @param result       - Array which first element will be used to output wrap
 462      *                     result object.
 463      * @return - Buffer with unwrapped data.
 464      * @throws SSLException - thrown on engine errors.
 465      */
 466     public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper,
 467                                       ByteBuffer net,
 468                                       SSLEngineResult.Status wantedStatus,
 469                                       SSLEngineResult[] result)
 470             throws SSLException {
 471         ByteBuffer app = ByteBuffer.allocate(engine.getSession()
 472                 .getApplicationBufferSize());
 473         int length = net.remaining();
 474         System.out.println(unwrapper + " unwrapping "
 475                 + length + " bytes...");
 476         SSLEngineResult r = engine.unwrap(net, app);
 477         app.flip();
 478         System.out.println(unwrapper + " handshake status is "
 479                 + engine.getHandshakeStatus());
 480         checkResult(r, wantedStatus);
 481         if (result != null && result.length > 0) {
 482             result[0] = r;
 483         }
 484         return app;
 485     }
 486 
 487     /**
 488      * Does the handshake of the two specified engines according to the
 489      * {@code mode} specified.
 490      *
 491      * @param clientEngine  - Client SSLEngine.
 492      * @param serverEngine  - Server SSLEngine.
 493      * @param maxPacketSize - Maximum packet size for MFLN of zero for no limit.
 494      * @param mode          - Handshake mode according to {@link HandshakeMode} enum.
 495      * @throws SSLException - thrown on engine errors.
 496      */
 497     public static void doHandshake(SSLEngine clientEngine,
 498                                    SSLEngine serverEngine,
 499                                    int maxPacketSize, HandshakeMode mode)
 500             throws SSLException {
 501         doHandshake(clientEngine, serverEngine, maxPacketSize, mode, false);
 502     }
 503 
 504     /**
 505      * Does the handshake of the two specified engines according to the
 506      * {@code mode} specified.
 507      *
 508      * @param clientEngine          - Client SSLEngine.
 509      * @param serverEngine          - Server SSLEngine.
 510      * @param maxPacketSize         - Maximum packet size for MFLN of zero for no limit.
 511      * @param mode                  - Handshake mode according to {@link HandshakeMode} enum.
 512      * @param enableReplicatedPacks - Set {@code true} to enable replicated
 513      *                              packet sending.
 514      * @throws SSLException - thrown on engine errors.
 515      */
 516     public static void doHandshake(SSLEngine clientEngine,
 517                                    SSLEngine serverEngine, int maxPacketSize,
 518                                    HandshakeMode mode,
 519                                    boolean enableReplicatedPacks)
 520             throws SSLException {
 521         System.out.println("================================================="
 522                 + "===========");
 523         System.out.println("Starting handshake " + mode.name());
 524         int loop = 0;
 525         if (maxPacketSize < 0) {
 526             throw new Error("Test issue: maxPacketSize is less than zero!");
 527         }
 528         SSLParameters params = clientEngine.getSSLParameters();
 529         params.setMaximumPacketSize(maxPacketSize);
 530         clientEngine.setSSLParameters(params);
 531         params = serverEngine.getSSLParameters();
 532         params.setMaximumPacketSize(maxPacketSize);
 533         serverEngine.setSSLParameters(params);
 534         SSLEngine firstEngine;
 535         SSLEngine secondEngine;
 536         switch (mode) {
 537             case INITIAL_HANDSHAKE:
 538                 firstEngine = clientEngine;
 539                 secondEngine = serverEngine;
 540                 doUnwrapForNotHandshakingStatus = false;
 541                 clientEngine.beginHandshake();
 542                 serverEngine.beginHandshake();
 543                 break;
 544             case REHANDSHAKE_BEGIN_CLIENT:
 545                 firstEngine = clientEngine;
 546                 secondEngine = serverEngine;
 547                 doUnwrapForNotHandshakingStatus = true;
 548                 clientEngine.beginHandshake();
 549                 break;
 550             case REHANDSHAKE_BEGIN_SERVER:
 551                 firstEngine = serverEngine;
 552                 secondEngine = clientEngine;
 553                 doUnwrapForNotHandshakingStatus = true;
 554                 serverEngine.beginHandshake();
 555                 break;
 556             default:
 557                 throw new Error("Test issue: unknown handshake mode");
 558         }
 559         endHandshakeLoop = false;
 560         while (!endHandshakeLoop) {
 561             if (++loop > MAX_HANDSHAKE_LOOPS) {
 562                 throw new Error("Too much loops for handshaking");
 563             }
 564             System.out.println("==============================================");
 565             System.out.println("Handshake loop " + loop);
 566             SSLEngineResult.HandshakeStatus clientHSStatus
 567                     = clientEngine.getHandshakeStatus();
 568             SSLEngineResult.HandshakeStatus serverHSStatus
 569                     = serverEngine.getHandshakeStatus();
 570             System.out.println("Client handshake status "
 571                     + clientHSStatus.name());
 572             System.out.println("Server handshake status "
 573                     + serverHSStatus.name());
 574             handshakeProcess(firstEngine, secondEngine, maxPacketSize,
 575                     enableReplicatedPacks);
 576             handshakeProcess(secondEngine, firstEngine, maxPacketSize,
 577                     enableReplicatedPacks);
 578         }
 579     }
 580 
 581     /**
 582      * Routine to send application data from one SSLEngine to another.
 583      *
 584      * @param fromEngine - Sending engine.
 585      * @param toEngine   - Receiving engine.
 586      * @return - Result of unwrap method of the receiving engine.
 587      * @throws SSLException - thrown on engine errors.
 588      */
 589     public static SSLEngineResult sendApplicationData(SSLEngine fromEngine,
 590                                                       SSLEngine toEngine)
 591             throws SSLException {
 592         String sender = null;
 593         String reciever = null;
 594         String excMsgSent = EXCHANGE_MSG_SENT;
 595         if (fromEngine.getUseClientMode() && !toEngine.getUseClientMode()) {
 596             sender = "Client";
 597             reciever = "Server";
 598             excMsgSent += " Client.";
 599         } else if (toEngine.getUseClientMode() && !fromEngine.getUseClientMode()) {
 600             sender = "Server";
 601             reciever = "Client";
 602             excMsgSent += " Server.";
 603         } else {
 604             throw new Error("Test issue: both engines are in the same mode");
 605         }
 606         System.out.println("================================================="
 607                 + "===========");
 608         System.out.println("Trying to send application data from " + sender
 609                 + " to " + reciever);
 610         ByteBuffer clientAppSent
 611                 = ByteBuffer.wrap(excMsgSent.getBytes());
 612         net = doWrap(fromEngine, sender, 0, clientAppSent);
 613         SSLEngineResult[] r = new SSLEngineResult[1];
 614         ByteBuffer serverAppRecv = doUnWrap(toEngine, reciever, net, r);
 615         byte[] serverAppRecvTrunc = Arrays.copyOf(serverAppRecv.array(),
 616                 serverAppRecv.limit());
 617         String msgRecv = new String(serverAppRecvTrunc);
 618         if (!msgRecv.equals(excMsgSent)) {
 619             throw new AssertionError(sender + " to " + reciever
 620                     + ": application data"
 621                     + " has been altered while sending."
 622                     + " Message sent: " + "\"" + excMsgSent + "\"."
 623                     + " Message recieved: " + "\"" + msgRecv + "\".");
 624         }
 625         System.out.println("Successful sending application data from " + sender
 626                 + " to " + reciever);
 627         return r[0];
 628     }
 629 
 630     /**
 631      * Close engines by sending "close outbound" message from one SSLEngine to
 632      * another.
 633      *
 634      * @param fromEngine - Sending engine.
 635      * @param toEngine   - Receiving engine.
 636      * @throws SSLException - thrown on engine errors.
 637      */
 638     public static void closeEngines(SSLEngine fromEngine,
 639                                     SSLEngine toEngine) throws SSLException {
 640         String from = null;
 641         String to = null;
 642         ByteBuffer app;
 643         if (fromEngine.getUseClientMode() && !toEngine.getUseClientMode()) {
 644             from = "Client";
 645             to = "Server";
 646         } else if (toEngine.getUseClientMode() && !fromEngine.getUseClientMode()) {
 647             from = "Server";
 648             to = "Client";
 649         } else {
 650             throw new Error("Both engines are in the same mode");
 651         }
 652         System.out.println("=========================================================");
 653         System.out.println("Trying to close engines from " + from + " to " + to);
 654         // Sending close outbound request to peer
 655         fromEngine.closeOutbound();
 656         app = ByteBuffer.allocate(fromEngine.getSession().getApplicationBufferSize());
 657         net = doWrap(fromEngine, from, 0, app, SSLEngineResult.Status.CLOSED);
 658         doUnWrap(toEngine, to, net, SSLEngineResult.Status.CLOSED);
 659         app = ByteBuffer.allocate(fromEngine.getSession().getApplicationBufferSize());
 660         net = doWrap(toEngine, to, 0, app, SSLEngineResult.Status.CLOSED);
 661         doUnWrap(fromEngine, from, net, SSLEngineResult.Status.CLOSED);
 662         if (!toEngine.isInboundDone()) {
 663             throw new AssertionError(from + " sent close request to " + to
 664                     + ", but " + to + "did not close inbound.");
 665         }
 666         // Executing close inbound
 667         fromEngine.closeInbound();
 668         app = ByteBuffer.allocate(fromEngine.getSession().getApplicationBufferSize());
 669         net = doWrap(fromEngine, from, 0, app, SSLEngineResult.Status.CLOSED);
 670         doUnWrap(toEngine, to, net, SSLEngineResult.Status.CLOSED);
 671         if (!toEngine.isOutboundDone()) {
 672             throw new AssertionError(from + "sent close request to " + to
 673                     + ", but " + to + "did not close outbound.");
 674         }
 675         System.out.println("Successful closing from " + from + " to " + to);
 676     }
 677 
 678     /**
 679      * Runs the same test case for all given {@code ciphers}. Method counts all
 680      * failures and throws {@code AssertionError} if one or more tests fail.
 681      *
 682      * @param ciphers - Ciphers that should be tested.
 683      */
 684     public void runTests(Ciphers ciphers) {
 685         int total = ciphers.ciphers.length;
 686         int failed = testSomeCiphers(ciphers);
 687         if (failed > 0) {
 688             throw new AssertionError("" + failed + " of " + total
 689                     + " tests failed!");
 690         }
 691         System.out.println("All tests passed!");
 692     }
 693 
 694     /**
 695      * Runs test cases for ciphers defined by the test mode.
 696      */
 697     public void runTests() {
 698         switch (TEST_MODE) {
 699             case "norm":
 700             case "norm_sni":
 701                 switch (TESTED_SECURITY_PROTOCOL) {
 702                     case "DTLSv1.0":
 703                     case "TLSv1":
 704                     case "TLSv1.1":
 705                         runTests(Ciphers.SUPPORTED_NON_KRB_NON_SHA_CIPHERS);
 706                         break;
 707                     default:
 708                         runTests(Ciphers.SUPPORTED_NON_KRB_CIPHERS);
 709                 }
 710                 break;
 711             case "krb":
 712                 runTests(Ciphers.SUPPORTED_KRB_CIPHERS);
 713                 break;
 714             default:
 715                 throw new Error("Test error: unexpected test mode: " + TEST_MODE);
 716         }
 717     }
 718 
 719     /**
 720      * Returns maxPacketSize value used for MFLN extension testing
 721      *
 722      * @return - MLFN extension max packet size.
 723      */
 724     public int getMaxPacketSize() {
 725         return maxPacketSize;
 726     }
 727 
 728     /**
 729      * Checks that status of result {@code r} is {@code wantedStatus}.
 730      *
 731      * @param r            - Result.
 732      * @param wantedStatus - Wanted status of the result.
 733      * @throws AssertionError - if status or {@code r} is not
 734      *                        {@code wantedStatus}.
 735      */
 736     public static void checkResult(SSLEngineResult r,
 737                                    SSLEngineResult.Status wantedStatus) {
 738         SSLEngineResult.Status rs = r.getStatus();
 739         if (!rs.equals(wantedStatus)) {
 740             throw new AssertionError("Unexpected status " + rs.name()
 741                     + ", should be " + wantedStatus.name());
 742         }
 743     }
 744 
 745     /**
 746      * Returns SSLContext with TESTED_SECURITY_PROTOCOL protocol and sets up keys.
 747      *
 748      * @return - SSLContext with a protocol specified by TESTED_SECURITY_PROTOCOL.
 749      */
 750     public static SSLContext getContext() {
 751         try {
 752             java.security.Security.setProperty("jdk.tls.disabledAlgorithms", "");
 753             java.security.Security.setProperty("jdk.certpath.disabledAlgorithms", "");
 754             KeyStore ks = KeyStore.getInstance("JKS");
 755             KeyStore ts = KeyStore.getInstance("JKS");
 756             char[] passphrase = PASSWD.toCharArray();
 757             try (FileInputStream keyFileStream = new FileInputStream(KEY_FILE_NAME)) {
 758                 ks.load(keyFileStream, passphrase);
 759             }
 760             try (FileInputStream trustFileStream = new FileInputStream(TRUST_FILE_NAME)) {
 761                 ts.load(trustFileStream, passphrase);
 762             }
 763             KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
 764             kmf.init(ks, passphrase);
 765             TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
 766             tmf.init(ts);
 767             SSLContext sslCtx = SSLContext.getInstance(TESTED_SECURITY_PROTOCOL);
 768             sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
 769             return sslCtx;
 770         } catch (KeyStoreException | IOException | NoSuchAlgorithmException |
 771                 CertificateException | UnrecoverableKeyException |
 772                 KeyManagementException ex) {
 773             throw new Error("Unexpected exception", ex);
 774         }
 775     }
 776 
 777     /**
 778      * Sets up and starts kerberos KDC server.
 779      */
 780     public static void setUpAndStartKDC() {
 781         String servicePrincipal = "host/" + SERVER_NAME + "@" + KRB_REALM;
 782         Map<String, String> principals = new HashMap<>();
 783         principals.put(KRB_USER_PRINCIPAL, KRB_USER_PASSWORD);
 784         principals.put(KRBTGT_PRINCIPAL, null);
 785         principals.put(servicePrincipal, null);
 786         System.setProperty("java.security.krb5.conf", KRB5_CONF_FILENAME);
 787         startKDC(KRB_REALM, principals, KTAB_FILENAME);
 788         System.setProperty("java.security.auth.login.config",
 789                 TEST_SRC + FS + JAAS_CONF_FILE);
 790         System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
 791     }
 792 
 793     /**
 794      * Sets up and starts kerberos KDC server if SSLEngineTestCase.TEST_MODE is "krb".
 795      */
 796     public static void setUpAndStartKDCIfNeeded() {
 797         if (TEST_MODE.equals("krb")) {
 798             setUpAndStartKDC();
 799         }
 800     }
 801 
 802     /**
 803      * Returns client ssl engine.
 804      *
 805      * @param context - SSLContext to get SSLEngine from.
 806      * @param useSNI  - flag used to enable or disable using SNI extension.
 807      *                Needed for Kerberos.
 808      */
 809     public static SSLEngine getClientSSLEngine(SSLContext context, boolean useSNI) {
 810         SSLEngine clientEngine = context.createSSLEngine(HOST, 80);
 811         clientEngine.setUseClientMode(true);
 812         if (useSNI) {
 813             SNIHostName serverName = new SNIHostName(SERVER_NAME);
 814             List<SNIServerName> serverNames = new ArrayList<>();
 815             serverNames.add(serverName);
 816             SSLParameters params = clientEngine.getSSLParameters();
 817             params.setServerNames(serverNames);
 818             clientEngine.setSSLParameters(params);
 819         }
 820         return clientEngine;
 821     }
 822 
 823     /**
 824      * Returns server ssl engine.
 825      *
 826      * @param context - SSLContext to get SSLEngine from.
 827      * @param useSNI  - flag used to enable or disable using SNI extension.
 828      *                Needed for Kerberos.
 829      */
 830     public static SSLEngine getServerSSLEngine(SSLContext context, boolean useSNI) {
 831         SSLEngine serverEngine = context.createSSLEngine();
 832         serverEngine.setUseClientMode(false);
 833         if (useSNI) {
 834             SNIMatcher matcher = SNIHostName.createSNIMatcher(SNI_PATTERN);
 835             List<SNIMatcher> matchers = new ArrayList<>();
 836             matchers.add(matcher);
 837             SSLParameters params = serverEngine.getSSLParameters();
 838             params.setSNIMatchers(matchers);
 839             serverEngine.setSSLParameters(params);
 840         }
 841         return serverEngine;
 842     }
 843 
 844     /**
 845      * Runs the test case for one cipher suite.
 846      *
 847      * @param cipher - Cipher suite name.
 848      * @throws SSLException - If tests fails.
 849      */
 850     abstract protected void testOneCipher(String cipher)
 851             throws SSLException;
 852 
 853     /**
 854      * Iterates through an array of ciphers and runs the same test case for
 855      * every entry.
 856      *
 857      * @param ciphers - Array of cipher names.
 858      * @return - Number of tests failed.
 859      */
 860     protected int testSomeCiphers(Ciphers ciphers) {
 861         int failedNum = 0;
 862         String description = ciphers.description;
 863         System.out.println("==================================================="
 864                 + "=========");
 865         System.out.println(description + " ciphers testing");
 866         System.out.println("==================================================="
 867                 + "=========");
 868         for (String cs : ciphers.ciphers) {
 869             System.out.println("-----------------------------------------------"
 870                     + "-------------");
 871             System.out.println("Testing cipher suite " + cs);
 872             System.out.println("-----------------------------------------------"
 873                     + "-------------");
 874             Throwable error = null;
 875             try {
 876                 testOneCipher(cs);
 877             } catch (Throwable t) {
 878                 error = t;
 879             }
 880             switch (ciphers) {
 881                 case SUPPORTED_NON_KRB_CIPHERS:
 882                 case SUPPORTED_NON_KRB_NON_SHA_CIPHERS:
 883                 case SUPPORTED_KRB_CIPHERS:
 884                 case ENABLED_NON_KRB_NOT_ANON_CIPHERS:
 885                     if (error != null) {
 886                         System.out.println("Test Failed: " + cs);
 887                         System.err.println("Test Exception for " + cs);
 888                         error.printStackTrace();
 889                         failedNum++;
 890                     } else {
 891                         System.out.println("Test Passed: " + cs);
 892                     }
 893                     break;
 894                 case UNSUPPORTED_CIPHERS:
 895                     if (error == null) {
 896                         System.out.println("Test Failed: " + cs);
 897                         System.err.println("Test for " + cs + " should have thrown"
 898                                 + " IllegalArgumentException, but it has not!");
 899                         failedNum++;
 900                     } else if (!(error instanceof IllegalArgumentException)) {
 901                         System.out.println("Test Failed: " + cs);
 902                         System.err.println("Test Exception for " + cs);
 903                         error.printStackTrace();
 904                         failedNum++;
 905                     } else {
 906                         System.out.println("Test Passed: " + cs);
 907                     }
 908                     break;
 909                 default:
 910                     throw new Error("Test issue: unexpected ciphers: "
 911                             + ciphers.name());
 912             }
 913         }
 914         return failedNum;
 915     }
 916 
 917     /**
 918      * Method used for the handshake routine.
 919      *
 920      * @param wrapingEngine         - Engine that is expected to wrap data.
 921      * @param unwrapingEngine       - Engine that is expected to unwrap data.
 922      * @param maxPacketSize         - Maximum packet size for MFLN of zero for no limit.
 923      * @param enableReplicatedPacks - Set {@code true} to enable replicated
 924      *                              packet sending.
 925      * @throws SSLException - thrown on engine errors.
 926      */
 927     private static void handshakeProcess(SSLEngine wrapingEngine,
 928                                          SSLEngine unwrapingEngine,
 929                                          int maxPacketSize,
 930                                          boolean enableReplicatedPacks)
 931             throws SSLException {
 932         SSLEngineResult.HandshakeStatus wrapingHSStatus = wrapingEngine
 933                 .getHandshakeStatus();
 934         SSLEngineResult.HandshakeStatus unwrapingHSStatus = unwrapingEngine
 935                 .getHandshakeStatus();
 936         SSLEngineResult r;
 937         String wrapper, unwrapper;
 938         if (wrapingEngine.getUseClientMode()
 939                 && !unwrapingEngine.getUseClientMode()) {
 940             wrapper = "Client";
 941             unwrapper = "Server";
 942         } else if (unwrapingEngine.getUseClientMode()
 943                 && !wrapingEngine.getUseClientMode()) {
 944             wrapper = "Server";
 945             unwrapper = "Client";
 946         } else {
 947             throw new Error("Both engines are in the same mode");
 948         }
 949         switch (wrapingHSStatus) {
 950             case NEED_WRAP:
 951                 if (enableReplicatedPacks) {
 952                     if (net != null) {
 953                         net.flip();
 954                         if (net.remaining() != 0) {
 955                             if (wrapingEngine.getUseClientMode()) {
 956                                 netReplicatedServer = net;
 957                             } else {
 958                                 netReplicatedClient = net;
 959                             }
 960                         }
 961                     }
 962                 }
 963                 ByteBuffer app = ByteBuffer.allocate(wrapingEngine.getSession()
 964                         .getApplicationBufferSize());
 965                 net = doWrap(wrapingEngine, wrapper, maxPacketSize, app);
 966             case NOT_HANDSHAKING:
 967                 switch (unwrapingHSStatus) {
 968                     case NEED_TASK:
 969                         runDelegatedTasks(unwrapingEngine);
 970                     case NEED_UNWRAP:
 971                         doUnWrap(unwrapingEngine, unwrapper, net);
 972                         if (enableReplicatedPacks) {
 973                             System.out.println("Unwrapping replicated packet...");
 974                             if (unwrapingEngine.getHandshakeStatus()
 975                                     .equals(SSLEngineResult.HandshakeStatus.NEED_TASK)) {
 976                                 runDelegatedTasks(unwrapingEngine);
 977                             }
 978                             runDelegatedTasks(unwrapingEngine);
 979                             ByteBuffer netReplicated;
 980                             if (unwrapingEngine.getUseClientMode()) {
 981                                 netReplicated = netReplicatedClient;
 982                             } else {
 983                                 netReplicated = netReplicatedServer;
 984                             }
 985                             if (netReplicated != null) {
 986                                 doUnWrap(unwrapingEngine, unwrapper, netReplicated);
 987                             } else {
 988                                 net.flip();
 989                                 doUnWrap(unwrapingEngine, unwrapper, net);
 990                             }
 991                         }
 992                         break;
 993                     case NEED_UNWRAP_AGAIN:
 994                         break;
 995                     case NOT_HANDSHAKING:
 996                         if (doUnwrapForNotHandshakingStatus) {
 997                             doUnWrap(unwrapingEngine, unwrapper, net);
 998                             doUnwrapForNotHandshakingStatus = false;
 999                             break;
1000                         } else {
1001                             endHandshakeLoop = true;
1002                         }
1003                         break;
1004                     default:
1005                         throw new Error("Unexpected unwraping engine handshake status "
1006                                 + unwrapingHSStatus.name());
1007                 }
1008                 break;
1009             case NEED_UNWRAP:
1010                 break;
1011             case NEED_UNWRAP_AGAIN:
1012                 net.flip();
1013                 doUnWrap(wrapingEngine, wrapper, net);
1014                 break;
1015             case NEED_TASK:
1016                 runDelegatedTasks(wrapingEngine);
1017                 break;
1018             default:
1019                 throw new Error("Unexpected wraping engine handshake status "
1020                         + wrapingHSStatus.name());
1021         }
1022     }
1023 
1024     private static void runDelegatedTasks(SSLEngine engine) {
1025         Runnable runnable;
1026         System.out.println("Running delegated tasks...");
1027         while ((runnable = engine.getDelegatedTask()) != null) {
1028             runnable.run();
1029         }
1030         SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
1031         if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
1032             throw new Error("Handshake shouldn't need additional tasks.");
1033         }
1034     }
1035 
1036     /**
1037      * Start a KDC server:
1038      * - create a KDC instance
1039      * - create Kerberos principals
1040      * - save Kerberos configuration
1041      * - save keys to keytab file
1042      * - no pre-auth is required
1043      */
1044     private static void startKDC(String realm, Map<String, String> principals,
1045                                  String ktab) {
1046         try {
1047             KDC kdc = KDC.create(realm, HOST, 0, true);
1048             kdc.setOption(KDC.Option.PREAUTH_REQUIRED, Boolean.FALSE);
1049             if (principals != null) {
1050                 principals.entrySet().stream().forEach((entry) -> {
1051                     String name = entry.getKey();
1052                     String password = entry.getValue();
1053                     if (password == null || password.isEmpty()) {
1054                         System.out.println("KDC: add a principal '" + name
1055                                 + "' with a random password");
1056                         kdc.addPrincipalRandKey(name);
1057                     } else {
1058                         System.out.println("KDC: add a principal '" + name
1059                                 + "' with '" + password + "' password");
1060                         kdc.addPrincipal(name, password.toCharArray());
1061                     }
1062                 });
1063             }
1064             KDC.saveConfig(KRB5_CONF_FILENAME, kdc);
1065             if (ktab != null) {
1066                 File ktabFile = new File(ktab);
1067                 if (ktabFile.exists()) {
1068                     System.out.println("KDC: append keys to an exising "
1069                             + "keytab file " + ktab);
1070                     kdc.appendKtab(ktab);
1071                 } else {
1072                     System.out.println("KDC: create a new keytab file "
1073                             + ktab);
1074                     kdc.writeKtab(ktab);
1075                 }
1076             }
1077             System.out.println("KDC: started on " + HOST + ":" + kdc.getPort()
1078                     + " with '" + realm + "' realm");
1079         } catch (Exception e) {
1080             throw new RuntimeException("KDC: unexpected exception", e);
1081         }
1082     }
1083 }