8167680 DTLS implementation bugs

   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.SSLSession;
  31 import javax.net.ssl.SSLEngineResult;
  32 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
  33 import javax.net.ssl.SSLException;
  34 import javax.net.ssl.SSLParameters;
  35 import javax.net.ssl.TrustManagerFactory;
  36 import java.io.File;
  37 import java.io.FileInputStream;
  38 import java.io.IOException;
  39 import java.nio.ByteBuffer;
  40 import java.security.KeyManagementException;
  41 import java.security.KeyStore;
  42 import java.security.KeyStoreException;
  43 import java.security.NoSuchAlgorithmException;
  44 import java.security.UnrecoverableKeyException;
  45 import java.security.cert.CertificateException;
  46 import java.util.ArrayList;
  47 import java.util.Arrays;
  48 import java.util.HashMap;
  49 import java.util.LinkedList;
  50 import java.util.List;
  51 import java.util.Map;
  52 
  53 /**
  54  * Basic class to inherit SSLEngine test cases from it. Tests apply for
  55  * the TLS or DTLS security protocols and their versions.
  56  */
  57 abstract public class SSLEngineTestCase {
  58 
  59     public enum Ciphers {
  60 
  61         /**
  62          * Ciphers supported by the tested SSLEngine without those with
  63          * kerberos authentication.
  64          */
  65         SUPPORTED_NON_KRB_CIPHERS(SSLEngineTestCase.SUPPORTED_NON_KRB_CIPHERS,
  66                 "Supported non kerberos"),
  67         /**
  68          * Ciphers supported by the tested SSLEngine without those with
  69          * kerberos authentication and without those with SHA256 ans SHA384.
  70          */
  71         SUPPORTED_NON_KRB_NON_SHA_CIPHERS(
  72                 SSLEngineTestCase.SUPPORTED_NON_KRB_NON_SHA_CIPHERS,
  73                 "Supported non kerberos non SHA256 and SHA384"),
  74         /**
  75          * Ciphers supported by the tested SSLEngine with kerberos
  76          * authentication.
  77          */
  78         SUPPORTED_KRB_CIPHERS(SSLEngineTestCase.SUPPORTED_KRB_CIPHERS,
  79                 "Supported kerberos"),
  80         /**
  81          * Ciphers enabled by default for the tested SSLEngine without kerberos
  82          * and anon.
  83          */
  84         ENABLED_NON_KRB_NOT_ANON_CIPHERS(
  85                 SSLEngineTestCase.ENABLED_NON_KRB_NOT_ANON_CIPHERS,
  86                 "Enabled by default non kerberos not anonymous"),
  87         /**
  88          * Ciphers unsupported by the tested SSLEngine.
  89          */
  90         UNSUPPORTED_CIPHERS(SSLEngineTestCase.UNSUPPORTED_CIPHERS,
  91                 "Unsupported");
  92 
  93         Ciphers(String[] ciphers, String description) {
  94             this.ciphers = ciphers;
  95             this.description = description;
  96         }
  97 
  98         final String[] ciphers;
  99         final String description;
 100     }
 101 
 102     /**
 103      * Enumeration used to distinguish handshake mode in
 104      * {@link SSLEngineTestCase#doHandshake(javax.net.ssl.SSLEngine,
 105      * javax.net.ssl.SSLEngine, int, SSLEngineTestCase.HandshakeMode, boolean)
 106      * SSLEngineTestCase.doHandshake} method.
 107      */
 108     public enum HandshakeMode {
 109 
 110         /**
 111          * Initial handshake done for the first time: both engines call
 112          * {@link SSLEngine#beginHandshake()} method.
 113          */
 114         INITIAL_HANDSHAKE,
 115         /**
 116          * Repeated handshake done by client: client engine calls
 117          * {@link SSLEngine#beginHandshake()} method.
 118          */
 119         REHANDSHAKE_BEGIN_CLIENT,
 120         /**
 121          * Repeated handshake done by server: server engine calls
 122          * {@link SSLEngine#beginHandshake()} method.
 123          */
 124         REHANDSHAKE_BEGIN_SERVER;
 125     }
 126     /**
 127      * Security protocol to be tested: "TLS" or "DTLS" or their versions,
 128      * e.g. "TLSv1", "TLSv1.1", "TLSv1.2", "DTLSv1.0", "DTLSv1.2".
 129      */
 130     public static final String TESTED_SECURITY_PROTOCOL
 131             = System.getProperty("test.security.protocol", "TLS");
 132     /**
 133      * Test mode: "norm", "norm_sni" or "krb".
 134      * Modes "norm" and "norm_sni" are used to run
 135      * with all supported non-kerberos ciphers.
 136      * Mode "krb" is used to run with kerberos ciphers.
 137      */
 138     public static final String TEST_MODE
 139             = System.getProperty("test.mode", "norm");
 140 
 141     private static final String FS = System.getProperty("file.separator", "/");
 142     private static final String PATH_TO_STORES = ".." + FS + "etc";
 143     private static final String KEY_STORE_FILE = "keystore";
 144     private static final String TRUST_STORE_FILE = "truststore";
 145     private static final String PASSWD = "passphrase";
 146 
 147     private static final String KEY_FILE_NAME
 148             = System.getProperty("test.src", ".") + FS + PATH_TO_STORES
 149             + FS + KEY_STORE_FILE;
 150     private static final String TRUST_FILE_NAME
 151             = System.getProperty("test.src", ".") + FS + PATH_TO_STORES
 152             + FS + TRUST_STORE_FILE;
 153 
 154     // Need an enhancement to use none-static mutable global variables.
 155     private static ByteBuffer net;




 156     private static boolean doUnwrapForNotHandshakingStatus;
 157     private static boolean endHandshakeLoop = false;
 158 
 159     private static final int MAX_HANDSHAKE_LOOPS = 100;
 160     private static final String EXCHANGE_MSG_SENT = "Hello, peer!";
 161     private static final String TEST_SRC = System.getProperty("test.src", ".");
 162     private static final String KTAB_FILENAME = "krb5.keytab.data";
 163     private static final String KRB_REALM = "TEST.REALM";
 164     private static final String KRBTGT_PRINCIPAL = "krbtgt/" + KRB_REALM;
 165     private static final String KRB_USER = "USER";
 166     private static final String KRB_USER_PASSWORD = "password";
 167     private static final String KRB_USER_PRINCIPAL = KRB_USER + "@" + KRB_REALM;
 168     private static final String KRB5_CONF_FILENAME = "krb5.conf";
 169     private static final String PATH_TO_COMMON = ".." + FS + "TLSCommon";
 170     private static final String JAAS_CONF_FILE = PATH_TO_COMMON
 171             + FS + "jaas.conf";
 172     private static final int DELAY = 1000;
 173     private static final String HOST = "localhost";
 174     private static final String SERVER_NAME = "service.localhost";
 175     private static final String SNI_PATTERN = ".*";
 176 
 177     private static final String[] SUPPORTED_NON_KRB_CIPHERS;
 178 
 179     static {
 180         try {
 181             String[] allSupportedCiphers = getContext()
 182                     .createSSLEngine().getSupportedCipherSuites();
 183             List<String> supportedCiphersList = new LinkedList<>();
 184             for (String cipher : allSupportedCiphers) {
 185                 if (!cipher.contains("KRB5")
 186                     && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
 187 
 188                     supportedCiphersList.add(cipher);
 189                 }
 190             }
 191             SUPPORTED_NON_KRB_CIPHERS =
 192                     supportedCiphersList.toArray(new String[0]);
 193         } catch (Exception ex) {
 194             throw new Error("Unexpected issue", ex);
 195         }
 196     }
 197 
 198     private static final String[] SUPPORTED_NON_KRB_NON_SHA_CIPHERS;
 199 
 200     static {
 201         try {
 202             String[] allSupportedCiphers = getContext()
 203                     .createSSLEngine().getSupportedCipherSuites();
 204             List<String> supportedCiphersList = new LinkedList<>();
 205             for (String cipher : allSupportedCiphers) {
 206                 if (!cipher.contains("KRB5")
 207                         && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")
 208                         && !cipher.endsWith("_SHA256")
 209                         && !cipher.endsWith("_SHA384")) {
 210                     supportedCiphersList.add(cipher);
 211                 }
 212             }
 213             SUPPORTED_NON_KRB_NON_SHA_CIPHERS
 214                     = supportedCiphersList.toArray(new String[0]);
 215         } catch (Exception ex) {
 216             throw new Error("Unexpected issue", ex);
 217         }
 218     }
 219 
 220     private static final String[] SUPPORTED_KRB_CIPHERS;
 221 
 222     static {
 223         try {
 224             String[] allSupportedCiphers = getContext()
 225                     .createSSLEngine().getSupportedCipherSuites();
 226             List<String> supportedCiphersList = new LinkedList<>();
 227             for (String cipher : allSupportedCiphers) {
 228                 if (cipher.contains("KRB5")
 229                     && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
 230                     supportedCiphersList.add(cipher);
 231                 }
 232             }
 233             SUPPORTED_KRB_CIPHERS = supportedCiphersList.toArray(new String[0]);
 234         } catch (Exception ex) {
 235             throw new Error("Unexpected issue", ex);
 236         }
 237     }
 238 
 239     private static final String[] ENABLED_NON_KRB_NOT_ANON_CIPHERS;
 240 
 241     static {
 242         try {
 243             SSLEngine temporary = getContext().createSSLEngine();
 244             temporary.setUseClientMode(true);
 245             String[] enabledCiphers = temporary.getEnabledCipherSuites();
 246             List<String> enabledCiphersList = new LinkedList<>();
 247             for (String cipher : enabledCiphers) {
 248                 if (!cipher.contains("anon") && !cipher.contains("KRB5")
 249                     && !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
 250                     enabledCiphersList.add(cipher);
 251                 }
 252             }
 253             ENABLED_NON_KRB_NOT_ANON_CIPHERS =
 254                     enabledCiphersList.toArray(new String[0]);
 255         } catch (Exception ex) {
 256             throw new Error("Unexpected issue", ex);
 257         }
 258     }
 259 
 260     private static final String[] UNSUPPORTED_CIPHERS = {
 261             "SSL_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA",
 262             "SSL_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA",
 263             "SSL_DHE_DSS_WITH_RC4_128_SHA",
 264             "SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA",
 265             "SSL_DH_DSS_WITH_3DES_EDE_CBC_SHA",
 266             "SSL_DH_DSS_WITH_DES_CBC_SHA",
 267             "SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA",
 268             "SSL_DH_RSA_WITH_3DES_EDE_CBC_SHA",
 269             "SSL_DH_RSA_WITH_DES_CBC_SHA",
 270             "SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA",
 271             "SSL_FORTEZZA_DMS_WITH_NULL_SHA",
 272             "SSL_RSA_EXPORT1024_WITH_DES_CBC_SHA",
 273             "SSL_RSA_EXPORT1024_WITH_RC4_56_SHA",
 274             "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5",
 275             "SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA",
 276             "SSL_RSA_FIPS_WITH_DES_CBC_SHA",
 277             "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5",
 278             "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA",
 279             "TLS_KRB5_WITH_IDEA_CBC_MD5",
 280             "TLS_KRB5_WITH_IDEA_CBC_SHA",
 281             "SSL_RSA_WITH_IDEA_CBC_SHA",
 282             "TLS_DH_RSA_WITH_AES_128_GCM_SHA256",
 283             "TLS_DH_RSA_WITH_AES_256_GCM_SHA384",
 284             "TLS_DH_DSS_WITH_AES_128_GCM_SHA256",
 285             "TLS_DH_DSS_WITH_AES_256_GCM_SHA384"
 286     };
 287 
 288     private final int maxPacketSize;
 289 
 290     /**
 291      * Constructs test case with the given MFLN maxMacketSize.
 292      *
 293      * @param maxPacketSize - MLFN extension max packet size.
 294      */
 295     public SSLEngineTestCase(int maxPacketSize) {
 296         this.maxPacketSize = maxPacketSize;
 297     }
 298 
 299     /**
 300      * Constructs test case with {@code maxPacketSize = 0}.
 301      */
 302     public SSLEngineTestCase() {
 303         this.maxPacketSize = 0;
 304     }
 305 
 306     /**
 307      * Wraps data with the specified engine.
 308      *
 309      * @param engine        - SSLEngine that wraps data.
 310      * @param wrapper       - Set wrapper id, e.g. "server" of "client".
 311      *                        Used for logging only.
 312      * @param maxPacketSize - Max packet size to check that MFLN extension
 313      *                        works or zero for no check.
 314      * @param app           - Buffer with data to wrap.
 315      * @return - Buffer with wrapped data.
 316      * @throws SSLException - thrown on engine errors.
 317      */
 318     public static ByteBuffer doWrap(SSLEngine engine, String wrapper,
 319                                     int maxPacketSize, ByteBuffer app)
 320             throws SSLException {
 321         return doWrap(engine, wrapper, maxPacketSize,
 322                 app, SSLEngineResult.Status.OK, null);
 323     }
 324 
 325     /**
 326      * Wraps data with the specified engine.
 327      *
 328      * @param engine        - SSLEngine that wraps data.
 329      * @param wrapper       - Set wrapper id, e.g. "server" of "client".
 330      *                        Used for logging only.
 331      * @param maxPacketSize - Max packet size to check that MFLN extension
 332      *                        works or zero for no check.
 333      * @param app           - Buffer with data to wrap.
 334      * @param result        - Array which first element will be used to
 335      *                        output wrap result object.
 336      * @return - Buffer with wrapped data.
 337      * @throws SSLException - thrown on engine errors.
 338      */
 339     public static ByteBuffer doWrap(SSLEngine engine, String wrapper,
 340                                     int maxPacketSize, ByteBuffer app,
 341                                     SSLEngineResult[] result)
 342             throws SSLException {
 343         return doWrap(engine, wrapper, maxPacketSize,
 344                 app, SSLEngineResult.Status.OK, result);
 345     }
 346 
 347     /**
 348      * Wraps data with the specified engine.
 349      *
 350      * @param engine        - SSLEngine that wraps data.
 351      * @param wrapper       - Set wrapper id, e.g. "server" of "client".
 352      *                        Used for logging only.
 353      * @param maxPacketSize - Max packet size to check that MFLN extension
 354      *                        works or zero for no check.
 355      * @param app           - Buffer with data to wrap.
 356      * @param wantedStatus  - Specifies expected result status of wrapping.
 357      * @return - Buffer with wrapped data.
 358      * @throws SSLException - thrown on engine errors.
 359      */
 360     public static ByteBuffer doWrap(SSLEngine engine, String wrapper,
 361                                     int maxPacketSize, ByteBuffer app,
 362                                     SSLEngineResult.Status wantedStatus)
 363             throws SSLException {
 364         return doWrap(engine, wrapper, maxPacketSize,
 365                 app, wantedStatus, null);
 366     }
 367 
 368     /**
 369      * Wraps data with the specified engine.
 370      *
 371      * @param engine        - SSLEngine that wraps data.
 372      * @param wrapper       - Set wrapper id, e.g. "server" of "client".
 373      *                        Used for logging only.
 374      * @param maxPacketSize - Max packet size to check that MFLN extension
 375      *                        works or zero for no check.
 376      * @param app           - Buffer with data to wrap.
 377      * @param wantedStatus  - Specifies expected result status of wrapping.
 378      * @param result        - Array which first element will be used to output
 379      *                        wrap result object.
 380      * @return - Buffer with wrapped data.
 381      * @throws SSLException - thrown on engine errors.
 382      */
 383     public static ByteBuffer doWrap(SSLEngine engine, String wrapper,
 384                                     int maxPacketSize, ByteBuffer app,
 385                                     SSLEngineResult.Status wantedStatus,
 386                                     SSLEngineResult[] result)
 387             throws SSLException {
 388         ByteBuffer net = ByteBuffer.allocate(engine.getSession()
 389                 .getPacketBufferSize());
 390         SSLEngineResult r = engine.wrap(app, net);
 391         net.flip();
 392         int length = net.remaining();
 393         System.out.println(wrapper + " wrapped " + length + " bytes.");
 394         System.out.println(wrapper + " handshake status is "
 395                 + engine.getHandshakeStatus());
 396         if (maxPacketSize < length && maxPacketSize != 0) {
 397             throw new AssertionError("Handshake wrapped net buffer length "
 398                     + length + " exceeds maximum packet size "
 399                     + maxPacketSize);
 400         }
 401         checkResult(r, wantedStatus);
 402         if (result != null && result.length > 0) {
 403             result[0] = r;
 404         }
 405         return net;
 406     }
 407 
 408     /**
 409      * Unwraps data with the specified engine.
 410      *
 411      * @param engine    - SSLEngine that unwraps data.
 412      * @param unwrapper - Set unwrapper id, e.g. "server" of "client". Used for
 413      *                  logging only.
 414      * @param net       - Buffer with data to unwrap.
 415      * @return - Buffer with unwrapped data.
 416      * @throws SSLException - thrown on engine errors.
 417      */
 418     public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper,
 419             ByteBuffer net) throws SSLException {
 420         return doUnWrap(engine, unwrapper,
 421                 net, SSLEngineResult.Status.OK, null);
 422     }
 423 
 424     /**
 425      * Unwraps data with the specified engine.
 426      *
 427      * @param engine    - SSLEngine that unwraps data.
 428      * @param unwrapper - Set unwrapper id, e.g. "server" of "client". Used for
 429      *                  logging only.
 430      * @param net       - Buffer with data to unwrap.
 431      * @param result    - Array which first element will be used to output wrap
 432      *                  result object.
 433      * @return - Buffer with unwrapped data.
 434      * @throws SSLException - thrown on engine errors.
 435      */
 436     public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper,
 437             ByteBuffer net, SSLEngineResult[] result) throws SSLException {
 438         return doUnWrap(engine, unwrapper,
 439                 net, SSLEngineResult.Status.OK, result);
 440     }
 441 
 442     /**
 443      * Unwraps data with the specified engine.
 444      *
 445      * @param engine       - SSLEngine that unwraps data.
 446      * @param unwrapper    - Set unwrapper id, e.g. "server" of "client".
 447      *                     Used for logging only.
 448      * @param net          - Buffer with data to unwrap.
 449      * @param wantedStatus - Specifies expected result status of wrapping.
 450      * @return - Buffer with unwrapped data.
 451      * @throws SSLException - thrown on engine errors.
 452      */
 453     public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper,
 454             ByteBuffer net,
 455             SSLEngineResult.Status wantedStatus) throws SSLException {

 456         return doUnWrap(engine, unwrapper, net, wantedStatus, null);
 457     }
 458 
 459     /**
 460      * Unwraps data with the specified engine.
 461      *
 462      * @param engine       - SSLEngine that unwraps data.
 463      * @param unwrapper    - Set unwrapper id, e.g. "server" of "client".
 464      *                       Used for logging only.
 465      * @param net          - Buffer with data to unwrap.
 466      * @param wantedStatus - Specifies expected result status of wrapping.
 467      * @param result       - Array which first element will be used to output
 468      *                       wrap result object.
 469      * @return - Buffer with unwrapped data.
 470      * @throws SSLException - thrown on engine errors.
 471      */
 472     public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper,
 473             ByteBuffer net, SSLEngineResult.Status wantedStatus,
 474             SSLEngineResult[] result) throws SSLException {
 475 
 476         ByteBuffer app = ByteBuffer.allocate(
 477                 engine.getSession().getApplicationBufferSize());

 478         int length = net.remaining();
 479         System.out.println(unwrapper + " unwrapping " + length + " bytes...");

 480         SSLEngineResult r = engine.unwrap(net, app);
 481         app.flip();
 482         System.out.println(unwrapper + " handshake status is "
 483                 + engine.getHandshakeStatus());
 484         checkResult(r, wantedStatus);
 485         if (result != null && result.length > 0) {
 486             result[0] = r;
 487         }
 488         return app;
 489     }
 490 
 491     /**
 492      * Does the handshake of the two specified engines according to the
 493      * {@code mode} specified.
 494      *
 495      * @param clientEngine  - Client SSLEngine.
 496      * @param serverEngine  - Server SSLEngine.
 497      * @param maxPacketSize - Maximum packet size for MFLN of zero for no limit.
 498      * @param mode          - Handshake mode according to
 499      *                        {@link HandshakeMode} enum.
 500      * @throws SSLException - thrown on engine errors.
 501      */
 502     public static void doHandshake(SSLEngine clientEngine,
 503         SSLEngine serverEngine,
 504         int maxPacketSize, HandshakeMode mode) throws SSLException {
 505 
 506         doHandshake(clientEngine, serverEngine, maxPacketSize, mode, false);
 507     }
 508 
 509     /**
 510      * Does the handshake of the two specified engines according to the
 511      * {@code mode} specified.
 512      *
 513      * @param clientEngine          - Client SSLEngine.
 514      * @param serverEngine          - Server SSLEngine.
 515      * @param maxPacketSize         - Maximum packet size for MFLN of zero
 516      *                                for no limit.
 517      * @param mode                  - Handshake mode according to
 518      *                                {@link HandshakeMode} enum.
 519      * @param enableReplicatedPacks - Set {@code true} to enable replicated
 520      *                                packet sending.
 521      * @throws SSLException - thrown on engine errors.
 522      */
 523     public static void doHandshake(SSLEngine clientEngine,
 524             SSLEngine serverEngine, int maxPacketSize,
 525             HandshakeMode mode,
 526             boolean enableReplicatedPacks) throws SSLException {
 527 
 528         System.out.println("=============================================");

 529         System.out.println("Starting handshake " + mode.name());
 530         int loop = 0;
 531         if (maxPacketSize < 0) {
 532             throw new Error("Test issue: maxPacketSize is less than zero!");
 533         }
 534         SSLParameters params = clientEngine.getSSLParameters();
 535         params.setMaximumPacketSize(maxPacketSize);
 536         clientEngine.setSSLParameters(params);
 537         params = serverEngine.getSSLParameters();
 538         params.setMaximumPacketSize(maxPacketSize);
 539         serverEngine.setSSLParameters(params);
 540         SSLEngine firstEngine;
 541         SSLEngine secondEngine;
 542         switch (mode) {
 543             case INITIAL_HANDSHAKE:
 544                 firstEngine = clientEngine;
 545                 secondEngine = serverEngine;
 546                 doUnwrapForNotHandshakingStatus = false;
 547                 clientEngine.beginHandshake();
 548                 serverEngine.beginHandshake();
 549                 break;
 550             case REHANDSHAKE_BEGIN_CLIENT:
 551                 firstEngine = clientEngine;
 552                 secondEngine = serverEngine;
 553                 doUnwrapForNotHandshakingStatus = true;
 554                 clientEngine.beginHandshake();
 555                 break;
 556             case REHANDSHAKE_BEGIN_SERVER:
 557                 firstEngine = serverEngine;
 558                 secondEngine = clientEngine;
 559                 doUnwrapForNotHandshakingStatus = true;
 560                 serverEngine.beginHandshake();
 561                 break;
 562             default:
 563                 throw new Error("Test issue: unknown handshake mode");
 564         }
 565         endHandshakeLoop = false;
 566         while (!endHandshakeLoop) {
 567             if (++loop > MAX_HANDSHAKE_LOOPS) {
 568                 throw new Error("Too much loops for handshaking");
 569             }
 570             System.out.println("============================================");
 571             System.out.println("Handshake loop " + loop + ": round 1");
 572             System.out.println("==========================");







 573             handshakeProcess(firstEngine, secondEngine, maxPacketSize,
 574                     enableReplicatedPacks);
 575             if (endHandshakeLoop) {
 576                 break;
 577             }
 578             System.out.println("Handshake loop " + loop + ": round 2");
 579             System.out.println("==========================");
 580             handshakeProcess(secondEngine, firstEngine, maxPacketSize,
 581                     enableReplicatedPacks);
 582         }
 583     }
 584 
 585     /**
 586      * Routine to send application data from one SSLEngine to another.
 587      *
 588      * @param fromEngine - Sending engine.
 589      * @param toEngine   - Receiving engine.
 590      * @return - Result of unwrap method of the receiving engine.
 591      * @throws SSLException - thrown on engine errors.
 592      */
 593     public static SSLEngineResult sendApplicationData(SSLEngine fromEngine,
 594                                                       SSLEngine toEngine)
 595             throws SSLException {
 596         String sender = null;
 597         String reciever = null;
 598         String excMsgSent = EXCHANGE_MSG_SENT;
 599         if (fromEngine.getUseClientMode() && !toEngine.getUseClientMode()) {
 600             sender = "Client";
 601             reciever = "Server";
 602             excMsgSent += " Client.";
 603         } else if (toEngine.getUseClientMode() &&
 604                 !fromEngine.getUseClientMode()) {
 605             sender = "Server";
 606             reciever = "Client";
 607             excMsgSent += " Server.";
 608         } else {
 609             throw new Error("Test issue: both engines are in the same mode");
 610         }
 611         System.out.println("=============================================");

 612         System.out.println("Trying to send application data from " + sender
 613                 + " to " + reciever);
 614         ByteBuffer clientAppSent
 615                 = ByteBuffer.wrap(excMsgSent.getBytes());
 616         net = doWrap(fromEngine, sender, 0, clientAppSent);
 617         SSLEngineResult[] r = new SSLEngineResult[1];
 618         ByteBuffer serverAppRecv = doUnWrap(toEngine, reciever, net, r);
 619         byte[] serverAppRecvTrunc = Arrays.copyOf(serverAppRecv.array(),
 620                 serverAppRecv.limit());
 621         String msgRecv = new String(serverAppRecvTrunc);
 622         if (!msgRecv.equals(excMsgSent)) {
 623             throw new AssertionError(sender + " to " + reciever
 624                     + ": application data"
 625                     + " has been altered while sending."
 626                     + " Message sent: " + "\"" + excMsgSent + "\"."
 627                     + " Message recieved: " + "\"" + msgRecv + "\".");
 628         }
 629         System.out.println("Successful sending application data from " + sender
 630                 + " to " + reciever);
 631         return r[0];
 632     }
 633 
 634     /**
 635      * Close engines by sending "close outbound" message from one SSLEngine to
 636      * another.
 637      *
 638      * @param fromEngine - Sending engine.
 639      * @param toEngine   - Receiving engine.
 640      * @throws SSLException - thrown on engine errors.
 641      */
 642     public static void closeEngines(SSLEngine fromEngine,
 643                                     SSLEngine toEngine) throws SSLException {
 644         String from = null;
 645         String to = null;
 646         ByteBuffer app;
 647         if (fromEngine.getUseClientMode() && !toEngine.getUseClientMode()) {
 648             from = "Client";
 649             to = "Server";
 650         } else if (toEngine.getUseClientMode() &&
 651                 !fromEngine.getUseClientMode()) {
 652             from = "Server";
 653             to = "Client";
 654         } else {
 655             throw new Error("Both engines are in the same mode");
 656         }
 657         System.out.println("=============================================");
 658         System.out.println(
 659                 "Trying to close engines from " + from + " to " + to);
 660         // Sending close outbound request to peer
 661         fromEngine.closeOutbound();
 662         app = ByteBuffer.allocate(
 663                 fromEngine.getSession().getApplicationBufferSize());
 664         net = doWrap(fromEngine, from, 0, app, SSLEngineResult.Status.CLOSED);
 665         doUnWrap(toEngine, to, net, SSLEngineResult.Status.CLOSED);
 666         app = ByteBuffer.allocate(
 667                 fromEngine.getSession().getApplicationBufferSize());
 668         net = doWrap(toEngine, to, 0, app, SSLEngineResult.Status.CLOSED);
 669         doUnWrap(fromEngine, from, net, SSLEngineResult.Status.CLOSED);
 670         if (!toEngine.isInboundDone()) {
 671             throw new AssertionError(from + " sent close request to " + to
 672                     + ", but " + to + "did not close inbound.");
 673         }
 674         // Executing close inbound
 675         fromEngine.closeInbound();
 676         app = ByteBuffer.allocate(
 677                 fromEngine.getSession().getApplicationBufferSize());
 678         net = doWrap(fromEngine, from, 0, app, SSLEngineResult.Status.CLOSED);
 679         doUnWrap(toEngine, to, net, SSLEngineResult.Status.CLOSED);
 680         if (!toEngine.isOutboundDone()) {
 681             throw new AssertionError(from + "sent close request to " + to
 682                     + ", but " + to + "did not close outbound.");
 683         }
 684         System.out.println("Successful closing from " + from + " to " + to);
 685     }
 686 
 687     /**
 688      * Runs the same test case for all given {@code ciphers}. Method counts all
 689      * failures and throws {@code AssertionError} if one or more tests fail.
 690      *
 691      * @param ciphers - Ciphers that should be tested.
 692      */
 693     public void runTests(Ciphers ciphers) {
 694         int total = ciphers.ciphers.length;
 695         int failed = testSomeCiphers(ciphers);
 696         if (failed > 0) {
 697             throw new AssertionError("" + failed + " of " + total
 698                     + " tests failed!");
 699         }
 700         System.out.println("All tests passed!");
 701     }
 702 
 703     /**
 704      * Runs test cases for ciphers defined by the test mode.
 705      */
 706     public void runTests() {
 707         switch (TEST_MODE) {
 708             case "norm":
 709             case "norm_sni":
 710                 switch (TESTED_SECURITY_PROTOCOL) {
 711                     case "DTLSv1.0":
 712                     case "TLSv1":
 713                     case "TLSv1.1":
 714                         runTests(Ciphers.SUPPORTED_NON_KRB_NON_SHA_CIPHERS);
 715                         break;
 716                     default:
 717                         runTests(Ciphers.SUPPORTED_NON_KRB_CIPHERS);
 718                 }
 719                 break;
 720             case "krb":
 721                 runTests(Ciphers.SUPPORTED_KRB_CIPHERS);
 722                 break;
 723             default:
 724                 throw new Error(
 725                         "Test error: unexpected test mode: " + TEST_MODE);
 726         }
 727     }
 728 
 729     /**
 730      * Returns maxPacketSize value used for MFLN extension testing
 731      *
 732      * @return - MLFN extension max packet size.
 733      */
 734     public int getMaxPacketSize() {
 735         return maxPacketSize;
 736     }
 737 
 738     /**
 739      * Checks that status of result {@code r} is {@code wantedStatus}.
 740      *
 741      * @param r            - Result.
 742      * @param wantedStatus - Wanted status of the result.
 743      * @throws AssertionError - if status or {@code r} is not
 744      *                        {@code wantedStatus}.
 745      */
 746     public static void checkResult(SSLEngineResult r,
 747                                    SSLEngineResult.Status wantedStatus) {
 748         SSLEngineResult.Status rs = r.getStatus();
 749         if (!rs.equals(wantedStatus)) {
 750             throw new AssertionError("Unexpected status " + rs.name()
 751                     + ", should be " + wantedStatus.name());
 752         }
 753     }
 754 
 755     /**
 756      * Returns SSLContext with TESTED_SECURITY_PROTOCOL protocol and
 757      * sets up keys.
 758      *
 759      * @return - SSLContext with a protocol specified by
 760      *           TESTED_SECURITY_PROTOCOL.
 761      */
 762     public static SSLContext getContext() {
 763         try {
 764             java.security.Security.setProperty(
 765                     "jdk.tls.disabledAlgorithms", "");
 766             java.security.Security.setProperty(
 767                     "jdk.certpath.disabledAlgorithms", "");
 768             KeyStore ks = KeyStore.getInstance("JKS");
 769             KeyStore ts = KeyStore.getInstance("JKS");
 770             char[] passphrase = PASSWD.toCharArray();
 771             try (FileInputStream keyFileStream =
 772                     new FileInputStream(KEY_FILE_NAME)) {
 773                 ks.load(keyFileStream, passphrase);
 774             }
 775             try (FileInputStream trustFileStream =
 776                     new FileInputStream(TRUST_FILE_NAME)) {
 777                 ts.load(trustFileStream, passphrase);
 778             }
 779             KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
 780             kmf.init(ks, passphrase);
 781             TrustManagerFactory tmf =
 782                     TrustManagerFactory.getInstance("SunX509");
 783             tmf.init(ts);
 784             SSLContext sslCtx =
 785                     SSLContext.getInstance(TESTED_SECURITY_PROTOCOL);
 786             sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
 787             return sslCtx;
 788         } catch (KeyStoreException | IOException | NoSuchAlgorithmException |
 789                 CertificateException | UnrecoverableKeyException |
 790                 KeyManagementException ex) {
 791             throw new Error("Unexpected exception", ex);
 792         }
 793     }
 794 
 795     /**
 796      * Sets up and starts kerberos KDC server.
 797      */
 798     public static void setUpAndStartKDC() {
 799         String servicePrincipal = "host/" + SERVER_NAME + "@" + KRB_REALM;
 800         Map<String, String> principals = new HashMap<>();
 801         principals.put(KRB_USER_PRINCIPAL, KRB_USER_PASSWORD);
 802         principals.put(KRBTGT_PRINCIPAL, null);
 803         principals.put(servicePrincipal, null);
 804         System.setProperty("java.security.krb5.conf", KRB5_CONF_FILENAME);
 805         startKDC(KRB_REALM, principals, KTAB_FILENAME);
 806         System.setProperty("java.security.auth.login.config",
 807                 TEST_SRC + FS + JAAS_CONF_FILE);
 808         System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
 809     }
 810 
 811     /**
 812      * Sets up and starts kerberos KDC server if
 813      * SSLEngineTestCase.TEST_MODE is "krb".
 814      */
 815     public static void setUpAndStartKDCIfNeeded() {
 816         if (TEST_MODE.equals("krb")) {
 817             setUpAndStartKDC();
 818         }
 819     }
 820 
 821     /**
 822      * Returns client ssl engine.
 823      *
 824      * @param context - SSLContext to get SSLEngine from.
 825      * @param useSNI  - flag used to enable or disable using SNI extension.
 826      *                Needed for Kerberos.
 827      */
 828     public static SSLEngine getClientSSLEngine(
 829             SSLContext context, boolean useSNI) {
 830 
 831         SSLEngine clientEngine = context.createSSLEngine(HOST, 80);
 832         clientEngine.setUseClientMode(true);
 833         if (useSNI) {
 834             SNIHostName serverName = new SNIHostName(SERVER_NAME);
 835             List<SNIServerName> serverNames = new ArrayList<>();
 836             serverNames.add(serverName);
 837             SSLParameters params = clientEngine.getSSLParameters();
 838             params.setServerNames(serverNames);
 839             clientEngine.setSSLParameters(params);
 840         }
 841         return clientEngine;
 842     }
 843 
 844     /**
 845      * Returns server ssl engine.
 846      *
 847      * @param context - SSLContext to get SSLEngine from.
 848      * @param useSNI  - flag used to enable or disable using SNI extension.
 849      *                Needed for Kerberos.
 850      */
 851     public static SSLEngine getServerSSLEngine(
 852             SSLContext context, boolean useSNI) {
 853 
 854         SSLEngine serverEngine = context.createSSLEngine();
 855         serverEngine.setUseClientMode(false);
 856         if (useSNI) {
 857             SNIMatcher matcher = SNIHostName.createSNIMatcher(SNI_PATTERN);
 858             List<SNIMatcher> matchers = new ArrayList<>();
 859             matchers.add(matcher);
 860             SSLParameters params = serverEngine.getSSLParameters();
 861             params.setSNIMatchers(matchers);
 862             serverEngine.setSSLParameters(params);
 863         }
 864         return serverEngine;
 865     }
 866 
 867     /**
 868      * Runs the test case for one cipher suite.
 869      *
 870      * @param cipher - Cipher suite name.
 871      * @throws SSLException - If tests fails.
 872      */
 873     abstract protected void testOneCipher(String cipher)
 874             throws SSLException;
 875 
 876     /**
 877      * Iterates through an array of ciphers and runs the same test case for
 878      * every entry.
 879      *
 880      * @param ciphers - Array of cipher names.
 881      * @return - Number of tests failed.
 882      */
 883     protected int testSomeCiphers(Ciphers ciphers) {
 884         int failedNum = 0;
 885         String description = ciphers.description;
 886         System.out.println("===============================================");

 887         System.out.println(description + " ciphers testing");
 888         System.out.println("===========================================");

 889         for (String cs : ciphers.ciphers) {
 890             System.out.println("---------------------------------------");

 891             System.out.println("Testing cipher suite " + cs);
 892             System.out.println("---------------------------------------");

 893             Throwable error = null;
 894 
 895             // Reset global mutable static variables
 896             net = null;
 897             doUnwrapForNotHandshakingStatus = false;
 898             endHandshakeLoop = false;
 899 
 900             try {
 901                 testOneCipher(cs);
 902             } catch (Throwable t) {
 903                 error = t;
 904             }
 905             switch (ciphers) {
 906                 case SUPPORTED_NON_KRB_CIPHERS:
 907                 case SUPPORTED_NON_KRB_NON_SHA_CIPHERS:
 908                 case SUPPORTED_KRB_CIPHERS:
 909                 case ENABLED_NON_KRB_NOT_ANON_CIPHERS:
 910                     if (error != null) {
 911                         System.out.println("Test Failed: " + cs);
 912                         System.err.println("Test Exception for " + cs);
 913                         error.printStackTrace();
 914                         failedNum++;
 915                     } else {
 916                         System.out.println("Test Passed: " + cs);
 917                     }
 918                     break;
 919                 case UNSUPPORTED_CIPHERS:
 920                     if (error == null) {
 921                         System.out.println("Test Failed: " + cs);
 922                         System.err.println("Test for " + cs +
 923                                 " should have thrown " +
 924                                 "IllegalArgumentException, but it has not!");
 925                         failedNum++;
 926                     } else if (!(error instanceof IllegalArgumentException)) {
 927                         System.out.println("Test Failed: " + cs);
 928                         System.err.println("Test Exception for " + cs);
 929                         error.printStackTrace();
 930                         failedNum++;
 931                     } else {
 932                         System.out.println("Test Passed: " + cs);
 933                     }
 934                     break;
 935                 default:
 936                     throw new Error("Test issue: unexpected ciphers: "
 937                             + ciphers.name());
 938             }
 939         }
 940 
 941         return failedNum;
 942     }
 943 
 944     /**
 945      * Method used for the handshake routine.
 946      *
 947      * @param wrapingEngine         - Engine that is expected to wrap data.
 948      * @param unwrapingEngine       - Engine that is expected to unwrap data.
 949      * @param maxPacketSize         - Maximum packet size for MFLN of zero
 950      *                                for no limit.
 951      * @param enableReplicatedPacks - Set {@code true} to enable replicated
 952      *                                packet sending.
 953      * @throws SSLException - thrown on engine errors.
 954      */
 955     private static void handshakeProcess(SSLEngine wrapingEngine,
 956             SSLEngine unwrapingEngine,
 957             int maxPacketSize,
 958             boolean enableReplicatedPacks) throws SSLException {
 959 
 960         HandshakeStatus wrapingHSStatus = wrapingEngine.getHandshakeStatus();
 961         HandshakeStatus unwrapingHSStatus =
 962                 unwrapingEngine.getHandshakeStatus();

 963         SSLEngineResult r;
 964         String wrapper, unwrapper;
 965         if (wrapingEngine.getUseClientMode()
 966                 && !unwrapingEngine.getUseClientMode()) {
 967             wrapper = "Client";
 968             unwrapper = "Server";
 969         } else if (unwrapingEngine.getUseClientMode()
 970                 && !wrapingEngine.getUseClientMode()) {
 971             wrapper = "Server";
 972             unwrapper = "Client";
 973         } else {
 974             throw new Error("Both engines are in the same mode");
 975         }
 976         System.out.println(
 977                 wrapper + " handshake (wrap) status " + wrapingHSStatus);
 978         System.out.println(
 979                 unwrapper + " handshake (unwrap) status " + unwrapingHSStatus);
 980 
 981         ByteBuffer netReplicatedClient = null;
 982         ByteBuffer netReplicatedServer = null;
 983         switch (wrapingHSStatus) {
 984             case NEED_WRAP:
 985                 if (enableReplicatedPacks) {
 986                     if (net != null) {
 987                         net.flip();
 988                         if (net.remaining() != 0) {
 989                             if (wrapingEngine.getUseClientMode()) {
 990                                 netReplicatedServer = net;
 991                             } else {
 992                                 netReplicatedClient = net;
 993                             }
 994                         }
 995                     }
 996                 }
 997                 ByteBuffer app = ByteBuffer.allocate(
 998                         wrapingEngine.getSession().getApplicationBufferSize());
 999                 net = doWrap(wrapingEngine, wrapper, maxPacketSize, app);
1000                 wrapingHSStatus = wrapingEngine.getHandshakeStatus();
1001                 // No break, falling into unwrapping.
1002             case NOT_HANDSHAKING:
1003                 switch (unwrapingHSStatus) {
1004                     case NEED_TASK:
1005                         runDelegatedTasks(unwrapingEngine);
1006                     case NEED_UNWRAP:
1007                         doUnWrap(unwrapingEngine, unwrapper, net);
1008                         if (enableReplicatedPacks) {
1009                             System.out.println(unwrapper +
1010                                     " unwrapping replicated packet...");
1011                             if (unwrapingEngine.getHandshakeStatus()
1012                                     .equals(HandshakeStatus.NEED_TASK)) {
1013                                 runDelegatedTasks(unwrapingEngine);
1014                             }

1015                             ByteBuffer netReplicated;
1016                             if (unwrapingEngine.getUseClientMode()) {
1017                                 netReplicated = netReplicatedClient;
1018                             } else {
1019                                 netReplicated = netReplicatedServer;
1020                             }
1021                             if (netReplicated != null) {
1022                                 doUnWrap(unwrapingEngine,
1023                                         unwrapper, netReplicated);
1024                             } else {
1025                                 net.flip();
1026                                 doUnWrap(unwrapingEngine, unwrapper, net);
1027                             }
1028                         }
1029                         break;
1030                     case NEED_UNWRAP_AGAIN:
1031                         break;
1032                     case NOT_HANDSHAKING:
1033                         if (doUnwrapForNotHandshakingStatus) {
1034                             System.out.println("Not handshake status unwrap");
1035                             doUnWrap(unwrapingEngine, unwrapper, net);
1036                             doUnwrapForNotHandshakingStatus = false;
1037                             break;
1038                         } else {
1039                             if (wrapingHSStatus ==
1040                                         HandshakeStatus.NOT_HANDSHAKING) {
1041                                 System.out.println("Handshake is completed");
1042                                 endHandshakeLoop = true;
1043                             }
1044                         }
1045                         break;
1046                     case NEED_WRAP:
1047                         SSLSession session = unwrapingEngine.getSession();
1048                         int bufferSize = session.getApplicationBufferSize();
1049                         ByteBuffer b = ByteBuffer.allocate(bufferSize);
1050                         net = doWrap(unwrapingEngine,
1051                                         unwrapper, maxPacketSize, b);
1052                         unwrapingHSStatus =
1053                                 unwrapingEngine.getHandshakeStatus();
1054                         if ((wrapingHSStatus ==
1055                                     HandshakeStatus.NOT_HANDSHAKING) &&
1056                             (unwrapingHSStatus ==
1057                                     HandshakeStatus.NOT_HANDSHAKING)) {
1058 
1059                             System.out.println("Handshake is completed");
1060                             endHandshakeLoop = true;
1061                         }
1062 
1063                         break;
1064                     default:
1065                         throw new Error(
1066                                 "Unexpected unwraping engine handshake status "
1067                                 + unwrapingHSStatus.name());
1068                 }
1069                 break;
1070             case NEED_UNWRAP:
1071                 break;
1072             case NEED_UNWRAP_AGAIN:
1073                 net.flip();
1074                 doUnWrap(wrapingEngine, wrapper, net);
1075                 break;
1076             case NEED_TASK:
1077                 runDelegatedTasks(wrapingEngine);
1078                 break;
1079             default:
1080                 throw new Error("Unexpected wraping engine handshake status "
1081                         + wrapingHSStatus.name());
1082         }
1083     }
1084 
1085     private static void runDelegatedTasks(SSLEngine engine) {
1086         Runnable runnable;
1087         System.out.println("Running delegated tasks...");
1088         while ((runnable = engine.getDelegatedTask()) != null) {
1089             runnable.run();
1090         }
1091         HandshakeStatus hs = engine.getHandshakeStatus();
1092         if (hs == HandshakeStatus.NEED_TASK) {
1093             throw new Error("Handshake shouldn't need additional tasks.");
1094         }
1095     }
1096 
1097     /**
1098      * Start a KDC server:
1099      * - create a KDC instance
1100      * - create Kerberos principals
1101      * - save Kerberos configuration
1102      * - save keys to keytab file
1103      * - no pre-auth is required
1104      */
1105     private static void startKDC(String realm, Map<String, String> principals,
1106                                  String ktab) {
1107         try {
1108             KDC kdc = KDC.create(realm, HOST, 0, true);
1109             kdc.setOption(KDC.Option.PREAUTH_REQUIRED, Boolean.FALSE);
1110             if (principals != null) {
1111                 principals.entrySet().stream().forEach((entry) -> {
1112                     String name = entry.getKey();
1113                     String password = entry.getValue();
1114                     if (password == null || password.isEmpty()) {
1115                         System.out.println("KDC: add a principal '" + name
1116                                 + "' with a random password");
1117                         kdc.addPrincipalRandKey(name);
1118                     } else {
1119                         System.out.println("KDC: add a principal '" + name
1120                                 + "' with '" + password + "' password");
1121                         kdc.addPrincipal(name, password.toCharArray());
1122                     }
1123                 });
1124             }
1125             KDC.saveConfig(KRB5_CONF_FILENAME, kdc);
1126             if (ktab != null) {
1127                 File ktabFile = new File(ktab);
1128                 if (ktabFile.exists()) {
1129                     System.out.println("KDC: append keys to an exising "
1130                             + "keytab file " + ktab);
1131                     kdc.appendKtab(ktab);
1132                 } else {
1133                     System.out.println("KDC: create a new keytab file "
1134                             + ktab);
1135                     kdc.writeKtab(ktab);
1136                 }
1137             }
1138             System.out.println("KDC: started on " + HOST + ":" + kdc.getPort()
1139                     + " with '" + realm + "' realm");
1140         } catch (Exception e) {
1141             throw new RuntimeException("KDC: unexpected exception", e);
1142         }
1143     }
1144 }
--- EOF ---