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  * @run main/othervm -Dtest.security.protocol=DTLS
  40  *      -Dtest.mode=norm DTLSSequenceNumberTest
  41  * @run main/othervm -Dtest.security.protocol=DTLS
  42  *      -Dtest.mode=norm_sni DTLSSequenceNumberTest
  43  * @run main/othervm -Dtest.security.protocol=DTLS
  44  *      -Dtest.mode=krb DTLSSequenceNumberTest
  45  */
  46 
  47 import java.nio.ByteBuffer;
  48 import java.util.TreeMap;
  49 import javax.net.ssl.SSLContext;
  50 import javax.net.ssl.SSLEngine;
  51 import javax.net.ssl.SSLEngineResult;
  52 import javax.net.ssl.SSLException;
  53 import java.util.Random;
  54 import jdk.test.lib.RandomFactory;
  55 
  56 /**
  57  * Testing DTLS records sequence number property support in application data
  58  * exchange.
  59  */
  60 public class DTLSSequenceNumberTest extends SSLEngineTestCase {
  61 
  62     private final String BIG_MESSAGE = "Very very big message. One two three"
  63             + " four five six seven eight nine ten eleven twelve thirteen"
  64             + " fourteen fifteen sixteen seventeen eighteen nineteen twenty.";
  65     private final byte[] BIG_MESSAGE_BYTES = BIG_MESSAGE.getBytes();
  66     private final int PIECES_NUMBER = 15;
  67 
  68     public static void main(String[] args) {
  69         DTLSSequenceNumberTest test = new DTLSSequenceNumberTest();
  70         setUpAndStartKDCIfNeeded();
  71         test.runTests();
  72     }
  73 
  74     @Override
  75     protected void testOneCipher(String cipher) throws SSLException {
  76         SSLContext context = getContext();
  77         int maxPacketSize = getMaxPacketSize();
  78         boolean useSNI = !TEST_MODE.equals("norm");
  79         SSLEngine clientEngine = getClientSSLEngine(context, useSNI);
  80         SSLEngine serverEngine = getServerSSLEngine(context, useSNI);
  81         clientEngine.setEnabledCipherSuites(new String[]{cipher});
  82         serverEngine.setEnabledCipherSuites(new String[]{cipher});
  83         serverEngine.setNeedClientAuth(!cipher.contains("anon"));
  84         doHandshake(clientEngine, serverEngine, maxPacketSize,
  85                 HandshakeMode.INITIAL_HANDSHAKE);
  86         checkSeqNumPropertyWithAppDataSend(clientEngine, serverEngine);
  87         checkSeqNumPropertyWithAppDataSend(serverEngine, clientEngine);
  88     }
  89 
  90     private void checkSeqNumPropertyWithAppDataSend(SSLEngine sendEngine,
  91             SSLEngine recvEngine) throws SSLException {
  92         String sender, reciever;
  93         if (sendEngine.getUseClientMode() && !recvEngine.getUseClientMode()) {
  94             sender = "Client";
  95             reciever = "Server";
  96         } else if (recvEngine.getUseClientMode() && !sendEngine.getUseClientMode()) {
  97             sender = "Server";
  98             reciever = "Client";
  99         } else {
 100             throw new Error("Both engines are in the same mode");
 101         }
 102         System.out.println("================================================="
 103                 + "===========");
 104         System.out.println("Checking DTLS sequence number support"
 105                 + " by sending data from " + sender + " to " + reciever);
 106         ByteBuffer[] sentMessages = new ByteBuffer[PIECES_NUMBER];
 107         ByteBuffer[] netBuffers = new ByteBuffer[PIECES_NUMBER];
 108         TreeMap<Long, ByteBuffer> recvMap = new TreeMap<>(Long::compareUnsigned);
 109         int symbolsInAMessage;
 110         int symbolsInTheLastMessage;
 111         int[] recievingSequence = new int[PIECES_NUMBER];
 112         for (int i = 0; i < PIECES_NUMBER; i++) {
 113             recievingSequence[i] = i;
 114         }
 115         shuffleArray(recievingSequence);
 116         if (BIG_MESSAGE.length() % PIECES_NUMBER == 0) {
 117             symbolsInAMessage = BIG_MESSAGE.length() / PIECES_NUMBER;
 118             symbolsInTheLastMessage = symbolsInAMessage;
 119         } else {
 120             symbolsInAMessage = BIG_MESSAGE.length() / (PIECES_NUMBER - 1);
 121             symbolsInTheLastMessage = BIG_MESSAGE.length() % (PIECES_NUMBER - 1);
 122         }
 123         for (int i = 0; i < PIECES_NUMBER - 1; i++) {
 124             sentMessages[i] = ByteBuffer.wrap(BIG_MESSAGE_BYTES,
 125                     i * symbolsInAMessage, symbolsInAMessage);
 126         }
 127         sentMessages[PIECES_NUMBER - 1] = ByteBuffer.wrap(BIG_MESSAGE_BYTES,
 128                 (PIECES_NUMBER - 1) * symbolsInAMessage, symbolsInTheLastMessage);
 129         long prevSeqNum = 0L;
 130         //Wrapping massages in direct order
 131         for (int i = 0; i < PIECES_NUMBER; i++) {
 132             netBuffers[i] = ByteBuffer.allocate(sendEngine.getSession()
 133                     .getPacketBufferSize());
 134             SSLEngineResult[] r = new SSLEngineResult[1];
 135             netBuffers[i] = doWrap(sendEngine, sender, 0, sentMessages[i], r);
 136             long seqNum = r[0].sequenceNumber();
 137             if (Long.compareUnsigned(seqNum, prevSeqNum) <= 0) {
 138                 throw new AssertionError("Sequence number of the wrapped "
 139                         + "message is less or equal than that of the"
 140                         + " previous one! "
 141                         + "Was " + prevSeqNum + ", now " + seqNum + ".");
 142             }
 143             prevSeqNum = seqNum;
 144         }
 145         //Unwrapping messages in random order and trying to reconstruct order
 146         //from sequence number.
 147         for (int i = 0; i < PIECES_NUMBER; i++) {
 148             int recvNow = recievingSequence[i];
 149             SSLEngineResult[] r = new SSLEngineResult[1];
 150             ByteBuffer recvMassage = doUnWrap(recvEngine, reciever,
 151                     netBuffers[recvNow], r);
 152             long seqNum = r[0].sequenceNumber();
 153             recvMap.put(seqNum, recvMassage);
 154         }
 155         int mapSize = recvMap.size();
 156         if (mapSize != PIECES_NUMBER) {
 157             throw new AssertionError("The number of received massages "
 158                     + mapSize + " is not equal to the number of sent messages "
 159                     + PIECES_NUMBER + "!");
 160         }
 161         byte[] recvBigMsgBytes = new byte[BIG_MESSAGE_BYTES.length];
 162         int counter = 0;
 163         for (ByteBuffer msg : recvMap.values()) {
 164             System.arraycopy(msg.array(), 0, recvBigMsgBytes,
 165                     counter * symbolsInAMessage, msg.remaining());
 166             counter++;
 167         }
 168         String recvBigMsg = new String(recvBigMsgBytes);
 169         if (!recvBigMsg.equals(BIG_MESSAGE)) {
 170             throw new AssertionError("Received big message is not equal to"
 171                     + " one that was sent! Received message is: " + recvBigMsg);
 172         }
 173     }
 174 
 175     private static void shuffleArray(int[] ar) {
 176         final Random RNG = RandomFactory.getRandom();
 177         for (int i = ar.length - 1; i > 0; i--) {
 178             int index = RNG.nextInt(i + 1);
 179             int a = ar[index];
 180             ar[index] = ar[i];
 181             ar[i] = a;
 182         }
 183     }
 184 }