1 /*
   2  * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 8043758
  27  * @summary Testing DTLS records sequence number property support in application
  28  *          data exchange.
  29  * @key randomness
  30  * @library /sun/security/krb5/auto /test/lib /javax/net/ssl/TLSCommon
  31  * @modules java.security.jgss
  32  *          jdk.security.auth
  33  *          java.security.jgss/sun.security.krb5:+open
  34  *          java.security.jgss/sun.security.krb5.internal:+open
  35  *          java.security.jgss/sun.security.krb5.internal.ccache
  36  *          java.security.jgss/sun.security.krb5.internal.crypto
  37  *          java.security.jgss/sun.security.krb5.internal.ktab
  38  *          java.base/sun.security.util
  39  * @build jdk.test.lib.RandomFactory
  40  * @run main/othervm -Dtest.security.protocol=DTLS
  41  *      -Dtest.mode=norm DTLSSequenceNumberTest
  42  * @run main/othervm -Dtest.security.protocol=DTLS
  43  *      -Dtest.mode=norm_sni DTLSSequenceNumberTest
  44  * @run main/othervm -Dtest.security.protocol=DTLS
  45  *      -Dtest.mode=krb DTLSSequenceNumberTest
  46  */
  47 
  48 import java.nio.ByteBuffer;
  49 import java.util.TreeMap;
  50 import javax.net.ssl.SSLContext;
  51 import javax.net.ssl.SSLEngine;
  52 import javax.net.ssl.SSLEngineResult;
  53 import javax.net.ssl.SSLException;
  54 import java.util.Random;
  55 import jdk.test.lib.RandomFactory;
  56 
  57 /**
  58  * Testing DTLS records sequence number property support in application data
  59  * exchange.
  60  */
  61 public class DTLSSequenceNumberTest extends SSLEngineTestCase {
  62 
  63     private final String BIG_MESSAGE = "Very very big message. One two three"
  64             + " four five six seven eight nine ten eleven twelve thirteen"
  65             + " fourteen fifteen sixteen seventeen eighteen nineteen twenty.";
  66     private final byte[] BIG_MESSAGE_BYTES = BIG_MESSAGE.getBytes();
  67     private final int PIECES_NUMBER = 15;
  68 
  69     public static void main(String[] args) {
  70         DTLSSequenceNumberTest test = new DTLSSequenceNumberTest();
  71         setUpAndStartKDCIfNeeded();
  72         test.runTests();
  73     }
  74 
  75     @Override
  76     protected void testOneCipher(String cipher) throws SSLException {
  77         SSLContext context = getContext();
  78         int maxPacketSize = getMaxPacketSize();
  79         boolean useSNI = !TEST_MODE.equals("norm");
  80         SSLEngine clientEngine = getClientSSLEngine(context, useSNI);
  81         SSLEngine serverEngine = getServerSSLEngine(context, useSNI);
  82         clientEngine.setEnabledCipherSuites(new String[]{cipher});
  83         serverEngine.setEnabledCipherSuites(new String[]{cipher});
  84         serverEngine.setNeedClientAuth(!cipher.contains("anon"));
  85         doHandshake(clientEngine, serverEngine, maxPacketSize,
  86                 HandshakeMode.INITIAL_HANDSHAKE);
  87         checkSeqNumPropertyWithAppDataSend(clientEngine, serverEngine);
  88         checkSeqNumPropertyWithAppDataSend(serverEngine, clientEngine);
  89     }
  90 
  91     private void checkSeqNumPropertyWithAppDataSend(SSLEngine sendEngine,
  92             SSLEngine recvEngine) throws SSLException {
  93         String sender, reciever;
  94         if (sendEngine.getUseClientMode() && !recvEngine.getUseClientMode()) {
  95             sender = "Client";
  96             reciever = "Server";
  97         } else if (recvEngine.getUseClientMode() && !sendEngine.getUseClientMode()) {
  98             sender = "Server";
  99             reciever = "Client";
 100         } else {
 101             throw new Error("Both engines are in the same mode");
 102         }
 103         System.out.println("================================================="
 104                 + "===========");
 105         System.out.println("Checking DTLS sequence number support"
 106                 + " by sending data from " + sender + " to " + reciever);
 107         ByteBuffer[] sentMessages = new ByteBuffer[PIECES_NUMBER];
 108         ByteBuffer[] netBuffers = new ByteBuffer[PIECES_NUMBER];
 109         TreeMap<Long, ByteBuffer> recvMap = new TreeMap<>(Long::compareUnsigned);
 110         int symbolsInAMessage;
 111         int symbolsInTheLastMessage;
 112         int[] recievingSequence = new int[PIECES_NUMBER];
 113         for (int i = 0; i < PIECES_NUMBER; i++) {
 114             recievingSequence[i] = i;
 115         }
 116         shuffleArray(recievingSequence);
 117         if (BIG_MESSAGE.length() % PIECES_NUMBER == 0) {
 118             symbolsInAMessage = BIG_MESSAGE.length() / PIECES_NUMBER;
 119             symbolsInTheLastMessage = symbolsInAMessage;
 120         } else {
 121             symbolsInAMessage = BIG_MESSAGE.length() / (PIECES_NUMBER - 1);
 122             symbolsInTheLastMessage = BIG_MESSAGE.length() % (PIECES_NUMBER - 1);
 123         }
 124         for (int i = 0; i < PIECES_NUMBER - 1; i++) {
 125             sentMessages[i] = ByteBuffer.wrap(BIG_MESSAGE_BYTES,
 126                     i * symbolsInAMessage, symbolsInAMessage);
 127         }
 128         sentMessages[PIECES_NUMBER - 1] = ByteBuffer.wrap(BIG_MESSAGE_BYTES,
 129                 (PIECES_NUMBER - 1) * symbolsInAMessage, symbolsInTheLastMessage);
 130         long prevSeqNum = 0L;
 131         //Wrapping massages in direct order
 132         for (int i = 0; i < PIECES_NUMBER; i++) {
 133             netBuffers[i] = ByteBuffer.allocate(sendEngine.getSession()
 134                     .getPacketBufferSize());
 135             SSLEngineResult[] r = new SSLEngineResult[1];
 136             netBuffers[i] = doWrap(sendEngine, sender, 0, sentMessages[i], r);
 137             long seqNum = r[0].sequenceNumber();
 138             if (Long.compareUnsigned(seqNum, prevSeqNum) <= 0) {
 139                 throw new AssertionError("Sequence number of the wrapped "
 140                         + "message is less or equal than that of the"
 141                         + " previous one! "
 142                         + "Was " + prevSeqNum + ", now " + seqNum + ".");
 143             }
 144             prevSeqNum = seqNum;
 145         }
 146         //Unwrapping messages in random order and trying to reconstruct order
 147         //from sequence number.
 148         for (int i = 0; i < PIECES_NUMBER; i++) {
 149             int recvNow = recievingSequence[i];
 150             SSLEngineResult[] r = new SSLEngineResult[1];
 151             ByteBuffer recvMassage = doUnWrap(recvEngine, reciever,
 152                     netBuffers[recvNow], r);
 153             long seqNum = r[0].sequenceNumber();
 154             recvMap.put(seqNum, recvMassage);
 155         }
 156         int mapSize = recvMap.size();
 157         if (mapSize != PIECES_NUMBER) {
 158             throw new AssertionError("The number of received massages "
 159                     + mapSize + " is not equal to the number of sent messages "
 160                     + PIECES_NUMBER + "!");
 161         }
 162         byte[] recvBigMsgBytes = new byte[BIG_MESSAGE_BYTES.length];
 163         int counter = 0;
 164         for (ByteBuffer msg : recvMap.values()) {
 165             System.arraycopy(msg.array(), 0, recvBigMsgBytes,
 166                     counter * symbolsInAMessage, msg.remaining());
 167             counter++;
 168         }
 169         String recvBigMsg = new String(recvBigMsgBytes);
 170         if (!recvBigMsg.equals(BIG_MESSAGE)) {
 171             throw new AssertionError("Received big message is not equal to"
 172                     + " one that was sent! Received message is: " + recvBigMsg);
 173         }
 174     }
 175 
 176     private static void shuffleArray(int[] ar) {
 177         final Random RNG = RandomFactory.getRandom();
 178         for (int i = ar.length - 1; i > 0; i--) {
 179             int index = RNG.nextInt(i + 1);
 180             int a = ar[index];
 181             ar[index] = ar[i];
 182             ar[i] = a;
 183         }
 184     }
 185 }