1/*
2 * Copyright (c) 2015, 2016, 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 /lib/testlibrary /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
47import java.nio.ByteBuffer;
48import java.util.TreeMap;
49import javax.net.ssl.SSLContext;
50import javax.net.ssl.SSLEngine;
51import javax.net.ssl.SSLEngineResult;
52import javax.net.ssl.SSLException;
53import java.util.Random;
54import jdk.testlibrary.RandomFactory;
55
56/**
57 * Testing DTLS records sequence number property support in application data
58 * exchange.
59 */
60public 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}
185