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