8167680: DTLS implementation bugs
Reviewed-by: jnimeh, asmotrak
This commit is contained in:
parent
7b9f0bff77
commit
b8904d34fe
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -279,6 +279,16 @@ final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
|
||||
fragmenter = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
void launchRetransmission() {
|
||||
// Note: Please don't retransmit if there are handshake messages
|
||||
// or alerts waiting in the queue.
|
||||
if (((alertMemos == null) || alertMemos.isEmpty()) &&
|
||||
(fragmenter != null) && fragmenter.isRetransmittable()) {
|
||||
fragmenter.setRetransmission();
|
||||
}
|
||||
}
|
||||
|
||||
// buffered record fragment
|
||||
private static class RecordMemo {
|
||||
byte contentType;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -84,4 +84,18 @@ interface DTLSRecord extends Record {
|
||||
+ maxPadding // padding
|
||||
+ maxMacSize; // MAC
|
||||
|
||||
/*
|
||||
* Minimum record size of Certificate handshake message.
|
||||
* Client sends a certificate message containing no certificates if no
|
||||
* suitable certificate is available. That is, the certificate_list
|
||||
* structure has a length of zero.
|
||||
*
|
||||
* struct {
|
||||
* ASN.1Cert certificate_list<0..2^24-1>;
|
||||
* } Certificate;
|
||||
*/
|
||||
static final int minCertPlaintextSize =
|
||||
headerSize // record header
|
||||
+ handshakeHeaderSize // handshake header
|
||||
+ 3; // cert list length
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -144,6 +144,13 @@ public class Debug {
|
||||
System.err.println(prefix + ": "+message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a message to stdout.
|
||||
*/
|
||||
static void log(String message) {
|
||||
System.out.println(Thread.currentThread().getName() + ": " + message);
|
||||
}
|
||||
|
||||
/**
|
||||
* print a blank line to stderr that is prefixed with the prefix.
|
||||
*/
|
||||
@ -156,7 +163,6 @@ public class Debug {
|
||||
/**
|
||||
* print a message to stderr that is prefixed with the prefix.
|
||||
*/
|
||||
|
||||
public static void println(String prefix, String message)
|
||||
{
|
||||
System.err.println(prefix + ": "+message);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1996, 2013, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -194,6 +194,11 @@ abstract class OutputRecord extends ByteArrayOutputStream
|
||||
// blank
|
||||
}
|
||||
|
||||
// apply to DTLS SSLEngine
|
||||
void launchRetransmission() {
|
||||
// blank
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
if (!isClosed) {
|
||||
@ -224,6 +229,9 @@ abstract class OutputRecord extends ByteArrayOutputStream
|
||||
sequenceNumber = authenticator.sequenceNumber();
|
||||
}
|
||||
|
||||
// The sequence number may be shared for different purpose.
|
||||
boolean sharedSequenceNumber = false;
|
||||
|
||||
// "flip" but skip over header again, add MAC & encrypt
|
||||
if (authenticator instanceof MAC) {
|
||||
MAC signer = (MAC)authenticator;
|
||||
@ -243,6 +251,11 @@ abstract class OutputRecord extends ByteArrayOutputStream
|
||||
// reset the position and limit
|
||||
destination.limit(destination.position());
|
||||
destination.position(dstContent);
|
||||
|
||||
// The signer has used and increased the sequence number.
|
||||
if (isDTLS) {
|
||||
sharedSequenceNumber = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,6 +274,11 @@ abstract class OutputRecord extends ByteArrayOutputStream
|
||||
|
||||
// Encrypt may pad, so again the limit may be changed.
|
||||
encCipher.encrypt(destination, dstLim);
|
||||
|
||||
// The cipher has used and increased the sequence number.
|
||||
if (isDTLS && encCipher.isAEADMode()) {
|
||||
sharedSequenceNumber = true;
|
||||
}
|
||||
} else {
|
||||
destination.position(destination.limit());
|
||||
}
|
||||
@ -290,8 +308,10 @@ abstract class OutputRecord extends ByteArrayOutputStream
|
||||
destination.put(headerOffset + 11, (byte)(fragLen >> 8));
|
||||
destination.put(headerOffset + 12, (byte)fragLen);
|
||||
|
||||
// Increase the sequence number for next use.
|
||||
authenticator.increaseSequenceNumber();
|
||||
// Increase the sequence number for next use if it is not shared.
|
||||
if (!sharedSequenceNumber) {
|
||||
authenticator.increaseSequenceNumber();
|
||||
}
|
||||
}
|
||||
|
||||
// Update destination position to reflect the amount of data produced.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -38,7 +38,7 @@ final class Plaintext {
|
||||
byte majorVersion;
|
||||
byte minorVersion;
|
||||
int recordEpoch; // incremented on every cipher state change
|
||||
long recordSN;
|
||||
long recordSN; // contains epcoh number (epoch | sequence)
|
||||
ByteBuffer fragment; // null if need to be reassembled
|
||||
|
||||
HandshakeStatus handshakeStatus; // null if not used or not handshaking
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -994,7 +994,22 @@ public final class SSLEngineImpl extends SSLEngine {
|
||||
|
||||
// plainText should never be null for TLS protocols
|
||||
HandshakeStatus hsStatus = null;
|
||||
if (!isDTLS || plainText != null) {
|
||||
if (plainText == Plaintext.PLAINTEXT_NULL) {
|
||||
// Only happens for DTLS protocols.
|
||||
//
|
||||
// Received a retransmitted flight, and need to retransmit the
|
||||
// previous delivered handshake flight messages.
|
||||
if (enableRetransmissions) {
|
||||
if (debug != null && Debug.isOn("verbose")) {
|
||||
Debug.log(
|
||||
"Retransmit the previous handshake flight messages.");
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
outputRecord.launchRetransmission();
|
||||
}
|
||||
} // Otherwise, discard the retransmitted flight.
|
||||
} else if (!isDTLS || plainText != null) {
|
||||
hsStatus = processInputRecord(plainText, appData, offset, length);
|
||||
}
|
||||
|
||||
@ -1003,7 +1018,7 @@ public final class SSLEngineImpl extends SSLEngine {
|
||||
}
|
||||
|
||||
if (plainText == null) {
|
||||
plainText = new Plaintext();
|
||||
plainText = Plaintext.PLAINTEXT_NULL;
|
||||
}
|
||||
plainText.handshakeStatus = hsStatus;
|
||||
|
||||
@ -1378,7 +1393,8 @@ public final class SSLEngineImpl extends SSLEngine {
|
||||
// Acquire the buffered to-be-delivered records or retransmissions.
|
||||
//
|
||||
// May have buffered records, or need retransmission if handshaking.
|
||||
if (!outputRecord.isEmpty() || (handshaker != null)) {
|
||||
if (!outputRecord.isEmpty() ||
|
||||
(enableRetransmissions && handshaker != null)) {
|
||||
ciphertext = outputRecord.acquireCiphertext(netData);
|
||||
}
|
||||
|
||||
@ -1403,13 +1419,36 @@ public final class SSLEngineImpl extends SSLEngine {
|
||||
|
||||
HandshakeStatus hsStatus = null;
|
||||
Ciphertext.RecordType recordType = ciphertext.recordType;
|
||||
if ((handshaker != null) &&
|
||||
(recordType.contentType == Record.ct_handshake) &&
|
||||
(recordType.handshakeType == HandshakeMessage.ht_finished) &&
|
||||
handshaker.isDone() && outputRecord.isEmpty()) {
|
||||
if ((recordType.contentType == Record.ct_handshake) &&
|
||||
(recordType.handshakeType == HandshakeMessage.ht_finished) &&
|
||||
outputRecord.isEmpty()) {
|
||||
|
||||
hsStatus = finishHandshake();
|
||||
connectionState = cs_DATA;
|
||||
if (handshaker == null) {
|
||||
hsStatus = HandshakeStatus.FINISHED;
|
||||
} else if (handshaker.isDone()) {
|
||||
hsStatus = finishHandshake();
|
||||
connectionState = cs_DATA;
|
||||
|
||||
// Retransmit the last flight twice.
|
||||
//
|
||||
// The application data transactions may begin immediately
|
||||
// after the last flight. If the last flight get lost, the
|
||||
// application data may be discarded accordingly. As could
|
||||
// be an issue for some applications. This impact can be
|
||||
// mitigated by sending the last fligth twice.
|
||||
if (isDTLS && enableRetransmissions) {
|
||||
if (debug != null && Debug.isOn("verbose")) {
|
||||
Debug.log(
|
||||
"Retransmit the last flight messages.");
|
||||
}
|
||||
|
||||
synchronized (this) {
|
||||
outputRecord.launchRetransmission();
|
||||
}
|
||||
|
||||
hsStatus = HandshakeStatus.NEED_WRAP;
|
||||
}
|
||||
}
|
||||
} // Otherwise, the followed call to getHSStatus() will help.
|
||||
|
||||
/*
|
||||
|
@ -558,73 +558,6 @@ final class ServerHandshaker extends Handshaker {
|
||||
applicationProtocol = "";
|
||||
}
|
||||
|
||||
// cookie exchange
|
||||
if (isDTLS) {
|
||||
HelloCookieManager hcMgr = sslContext.getHelloCookieManager();
|
||||
if ((mesg.cookie == null) || (mesg.cookie.length == 0) ||
|
||||
(!hcMgr.isValid(mesg))) {
|
||||
|
||||
//
|
||||
// Perform cookie exchange for DTLS handshaking if no cookie
|
||||
// or the cookie is invalid in the ClientHello message.
|
||||
//
|
||||
HelloVerifyRequest m0 = new HelloVerifyRequest(hcMgr, mesg);
|
||||
|
||||
if (debug != null && Debug.isOn("handshake")) {
|
||||
m0.print(System.out);
|
||||
}
|
||||
|
||||
m0.write(output);
|
||||
handshakeState.update(m0, resumingSession);
|
||||
output.flush();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* FIRST, construct the ServerHello using the options and priorities
|
||||
* from the ClientHello. Update the (pending) cipher spec as we do
|
||||
* so, and save the client's version to protect against rollback
|
||||
* attacks.
|
||||
*
|
||||
* There are a bunch of minor tasks here, and one major one: deciding
|
||||
* if the short or the full handshake sequence will be used.
|
||||
*/
|
||||
ServerHello m1 = new ServerHello();
|
||||
|
||||
clientRequestedVersion = mesg.protocolVersion;
|
||||
|
||||
// select a proper protocol version.
|
||||
ProtocolVersion selectedVersion =
|
||||
selectProtocolVersion(clientRequestedVersion);
|
||||
if (selectedVersion == null ||
|
||||
selectedVersion.v == ProtocolVersion.SSL20Hello.v) {
|
||||
fatalSE(Alerts.alert_handshake_failure,
|
||||
"Client requested protocol " + clientRequestedVersion +
|
||||
" not enabled or not supported");
|
||||
}
|
||||
|
||||
handshakeHash.protocolDetermined(selectedVersion);
|
||||
setVersion(selectedVersion);
|
||||
|
||||
m1.protocolVersion = protocolVersion;
|
||||
|
||||
//
|
||||
// random ... save client and server values for later use
|
||||
// in computing the master secret (from pre-master secret)
|
||||
// and thence the other crypto keys.
|
||||
//
|
||||
// NOTE: this use of three inputs to generating _each_ set
|
||||
// of ciphers slows things down, but it does increase the
|
||||
// security since each connection in the session can hold
|
||||
// its own authenticated (and strong) keys. One could make
|
||||
// creation of a session a rare thing...
|
||||
//
|
||||
clnt_random = mesg.clnt_random;
|
||||
svr_random = new RandomCookie(sslContext.getSecureRandom());
|
||||
m1.svr_random = svr_random;
|
||||
|
||||
session = null; // forget about the current session
|
||||
//
|
||||
// Here we go down either of two paths: (a) the fast one, where
|
||||
@ -732,6 +665,73 @@ final class ServerHandshaker extends Handshaker {
|
||||
}
|
||||
} // else client did not try to resume
|
||||
|
||||
// cookie exchange
|
||||
if (isDTLS && !resumingSession) {
|
||||
HelloCookieManager hcMgr = sslContext.getHelloCookieManager();
|
||||
if ((mesg.cookie == null) || (mesg.cookie.length == 0) ||
|
||||
(!hcMgr.isValid(mesg))) {
|
||||
|
||||
//
|
||||
// Perform cookie exchange for DTLS handshaking if no cookie
|
||||
// or the cookie is invalid in the ClientHello message.
|
||||
//
|
||||
HelloVerifyRequest m0 = new HelloVerifyRequest(hcMgr, mesg);
|
||||
|
||||
if (debug != null && Debug.isOn("handshake")) {
|
||||
m0.print(System.out);
|
||||
}
|
||||
|
||||
m0.write(output);
|
||||
handshakeState.update(m0, resumingSession);
|
||||
output.flush();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* FIRST, construct the ServerHello using the options and priorities
|
||||
* from the ClientHello. Update the (pending) cipher spec as we do
|
||||
* so, and save the client's version to protect against rollback
|
||||
* attacks.
|
||||
*
|
||||
* There are a bunch of minor tasks here, and one major one: deciding
|
||||
* if the short or the full handshake sequence will be used.
|
||||
*/
|
||||
ServerHello m1 = new ServerHello();
|
||||
|
||||
clientRequestedVersion = mesg.protocolVersion;
|
||||
|
||||
// select a proper protocol version.
|
||||
ProtocolVersion selectedVersion =
|
||||
selectProtocolVersion(clientRequestedVersion);
|
||||
if (selectedVersion == null ||
|
||||
selectedVersion.v == ProtocolVersion.SSL20Hello.v) {
|
||||
fatalSE(Alerts.alert_handshake_failure,
|
||||
"Client requested protocol " + clientRequestedVersion +
|
||||
" not enabled or not supported");
|
||||
}
|
||||
|
||||
handshakeHash.protocolDetermined(selectedVersion);
|
||||
setVersion(selectedVersion);
|
||||
|
||||
m1.protocolVersion = protocolVersion;
|
||||
|
||||
//
|
||||
// random ... save client and server values for later use
|
||||
// in computing the master secret (from pre-master secret)
|
||||
// and thence the other crypto keys.
|
||||
//
|
||||
// NOTE: this use of three inputs to generating _each_ set
|
||||
// of ciphers slows things down, but it does increase the
|
||||
// security since each connection in the session can hold
|
||||
// its own authenticated (and strong) keys. One could make
|
||||
// creation of a session a rare thing...
|
||||
//
|
||||
clnt_random = mesg.clnt_random;
|
||||
svr_random = new RandomCookie(sslContext.getSecureRandom());
|
||||
m1.svr_random = svr_random;
|
||||
|
||||
//
|
||||
// If client hasn't specified a session we can resume, start a
|
||||
// new one and choose its cipher suite and compression options.
|
||||
|
@ -48,10 +48,6 @@ import sun.security.util.HexDumpEncoder;
|
||||
*/
|
||||
public class DTLSOverDatagram {
|
||||
|
||||
static {
|
||||
System.setProperty("javax.net.debug", "ssl");
|
||||
}
|
||||
|
||||
private static int MAX_HANDSHAKE_LOOPS = 200;
|
||||
private static int MAX_APP_READ_LOOPS = 60;
|
||||
private static int SOCKET_TIMEOUT = 10 * 1000; // in millis
|
||||
@ -160,6 +156,7 @@ public class DTLSOverDatagram {
|
||||
}
|
||||
|
||||
SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
|
||||
log(side, "=======handshake(" + loops + ", " + hs + ")=======");
|
||||
if (hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP ||
|
||||
hs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN) {
|
||||
|
||||
@ -239,6 +236,7 @@ public class DTLSOverDatagram {
|
||||
boolean finished = produceHandshakePackets(
|
||||
engine, peerAddr, side, packets);
|
||||
|
||||
log(side, "Produced " + packets.size() + " packets");
|
||||
for (DatagramPacket p : packets) {
|
||||
socket.send(p);
|
||||
}
|
||||
@ -252,14 +250,16 @@ public class DTLSOverDatagram {
|
||||
} else if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
|
||||
runDelegatedTasks(engine);
|
||||
} else if (hs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
|
||||
log(side, "Handshake status is NOT_HANDSHAKING, finish the loop");
|
||||
log(side,
|
||||
"Handshake status is NOT_HANDSHAKING, finish the loop");
|
||||
endLoops = true;
|
||||
} else if (hs == SSLEngineResult.HandshakeStatus.FINISHED) {
|
||||
throw new Exception(
|
||||
"Unexpected status, SSLEngine.getHandshakeStatus() "
|
||||
+ "shouldn't return FINISHED");
|
||||
} else {
|
||||
throw new Exception("Can't reach here, handshake status is " + hs);
|
||||
throw new Exception(
|
||||
"Can't reach here, handshake status is " + hs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -279,7 +279,9 @@ public class DTLSOverDatagram {
|
||||
log(side, "Negotiated cipher suite is " + session.getCipherSuite());
|
||||
|
||||
// handshake status should be NOT_HANDSHAKING
|
||||
// according to the spec, SSLEngine.getHandshakeStatus() can't return FINISHED
|
||||
//
|
||||
// According to the spec, SSLEngine.getHandshakeStatus() can't
|
||||
// return FINISHED.
|
||||
if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
|
||||
throw new Exception("Unexpected handshake status " + hs);
|
||||
}
|
||||
@ -348,13 +350,16 @@ public class DTLSOverDatagram {
|
||||
|
||||
SSLEngineResult.Status rs = r.getStatus();
|
||||
SSLEngineResult.HandshakeStatus hs = r.getHandshakeStatus();
|
||||
log(side, "====packet(" + loops + ", " + rs + ", " + hs + ")====");
|
||||
if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
|
||||
// the client maximum fragment size config does not work?
|
||||
throw new Exception("Buffer overflow: " +
|
||||
"incorrect server maximum fragment size");
|
||||
} else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
|
||||
log(side, "Produce handshake packets: BUFFER_UNDERFLOW occured");
|
||||
log(side, "Produce handshake packets: Handshake status: " + hs);
|
||||
log(side,
|
||||
"Produce handshake packets: BUFFER_UNDERFLOW occured");
|
||||
log(side,
|
||||
"Produce handshake packets: Handshake status: " + hs);
|
||||
// bad packet, or the client maximum fragment size
|
||||
// config does not work?
|
||||
if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
|
||||
@ -453,6 +458,53 @@ public class DTLSOverDatagram {
|
||||
return packets;
|
||||
}
|
||||
|
||||
// Get a datagram packet for the specified handshake type.
|
||||
static DatagramPacket getPacket(
|
||||
List<DatagramPacket> packets, byte handshakeType) {
|
||||
boolean matched = false;
|
||||
for (DatagramPacket packet : packets) {
|
||||
byte[] data = packet.getData();
|
||||
int offset = packet.getOffset();
|
||||
int length = packet.getLength();
|
||||
|
||||
// Normally, this pakcet should be a handshake message
|
||||
// record. However, even if the underlying platform
|
||||
// splits the record more, we don't really worry about
|
||||
// the improper packet loss because DTLS implementation
|
||||
// should be able to handle packet loss properly.
|
||||
//
|
||||
// See RFC 6347 for the detailed format of DTLS records.
|
||||
if (handshakeType == -1) { // ChangeCipherSpec
|
||||
// Is it a ChangeCipherSpec message?
|
||||
matched = (length == 14) && (data[offset] == 0x14);
|
||||
} else if ((length >= 25) && // 25: handshake mini size
|
||||
(data[offset] == 0x16)) { // a handshake message
|
||||
|
||||
// check epoch number for initial handshake only
|
||||
if (data[offset + 3] == 0x00) { // 3,4: epoch
|
||||
if (data[offset + 4] == 0x00) { // plaintext
|
||||
matched =
|
||||
(data[offset + 13] == handshakeType);
|
||||
} else { // cipherext
|
||||
// The 1st ciphertext is a Finished message.
|
||||
//
|
||||
// If it is not proposed to loss the Finished
|
||||
// message, it is not necessary to check the
|
||||
// following packets any mroe as a Finished
|
||||
// message is the last handshake message.
|
||||
matched = (handshakeType == 20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matched) {
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// run delegated tasks
|
||||
void runDelegatedTasks(SSLEngine engine) throws Exception {
|
||||
Runnable runnable;
|
||||
|
111
jdk/test/javax/net/ssl/DTLS/PacketLossRetransmission.java
Normal file
111
jdk/test/javax/net/ssl/DTLS/PacketLossRetransmission.java
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
// SunJSSE does not support dynamic system properties, no way to re-use
|
||||
// system properties in samevm/agentvm mode.
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8161086
|
||||
* @summary DTLS handshaking fails if some messages were lost
|
||||
* @modules java.base/sun.security.util
|
||||
* @build DTLSOverDatagram
|
||||
*
|
||||
* @run main/othervm PacketLossRetransmission client 0 hello_request
|
||||
* @run main/othervm PacketLossRetransmission client 1 client_hello
|
||||
* @run main/othervm PacketLossRetransmission client 2 server_hello
|
||||
* @run main/othervm PacketLossRetransmission client 3 hello_verify_request
|
||||
* @run main/othervm PacketLossRetransmission client 4 new_session_ticket
|
||||
* @run main/othervm PacketLossRetransmission client 11 certificate
|
||||
* @run main/othervm PacketLossRetransmission client 12 server_key_exchange
|
||||
* @run main/othervm PacketLossRetransmission client 13 certificate_request
|
||||
* @run main/othervm PacketLossRetransmission client 14 server_hello_done
|
||||
* @run main/othervm PacketLossRetransmission client 15 certificate_verify
|
||||
* @run main/othervm PacketLossRetransmission client 16 client_key_exchange
|
||||
* @run main/othervm PacketLossRetransmission client 20 finished
|
||||
* @run main/othervm PacketLossRetransmission client 21 certificate_url
|
||||
* @run main/othervm PacketLossRetransmission client 22 certificate_status
|
||||
* @run main/othervm PacketLossRetransmission client 23 supplemental_data
|
||||
* @run main/othervm PacketLossRetransmission client -1 change_cipher_spec
|
||||
* @run main/othervm PacketLossRetransmission server 0 hello_request
|
||||
* @run main/othervm PacketLossRetransmission server 1 client_hello
|
||||
* @run main/othervm PacketLossRetransmission server 2 server_hello
|
||||
* @run main/othervm PacketLossRetransmission server 3 hello_verify_request
|
||||
* @run main/othervm PacketLossRetransmission server 4 new_session_ticket
|
||||
* @run main/othervm PacketLossRetransmission server 11 certificate
|
||||
* @run main/othervm PacketLossRetransmission server 12 server_key_exchange
|
||||
* @run main/othervm PacketLossRetransmission server 13 certificate_request
|
||||
* @run main/othervm PacketLossRetransmission server 14 server_hello_done
|
||||
* @run main/othervm PacketLossRetransmission server 15 certificate_verify
|
||||
* @run main/othervm PacketLossRetransmission server 16 client_key_exchange
|
||||
* @run main/othervm PacketLossRetransmission server 20 finished
|
||||
* @run main/othervm PacketLossRetransmission server 21 certificate_url
|
||||
* @run main/othervm PacketLossRetransmission server 22 certificate_status
|
||||
* @run main/othervm PacketLossRetransmission server 23 supplemental_data
|
||||
* @run main/othervm PacketLossRetransmission server -1 change_cipher_spec
|
||||
*/
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.SocketAddress;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
/**
|
||||
* Test that DTLS implementation is able to do retransmission internally
|
||||
* automatically if packet get lost.
|
||||
*/
|
||||
public class PacketLossRetransmission extends DTLSOverDatagram {
|
||||
private static boolean isClient;
|
||||
private static byte handshakeType;
|
||||
|
||||
private boolean needPacketLoss = true;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
isClient = args[0].equals("client");
|
||||
handshakeType = Byte.valueOf(args[1]);
|
||||
|
||||
PacketLossRetransmission testCase = new PacketLossRetransmission();
|
||||
testCase.runTest(testCase);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean produceHandshakePackets(SSLEngine engine, SocketAddress socketAddr,
|
||||
String side, List<DatagramPacket> packets) throws Exception {
|
||||
|
||||
boolean finished = super.produceHandshakePackets(
|
||||
engine, socketAddr, side, packets);
|
||||
|
||||
if (needPacketLoss && (!(isClient ^ engine.getUseClientMode()))) {
|
||||
DatagramPacket packet = getPacket(packets, handshakeType);
|
||||
if (packet != null) {
|
||||
needPacketLoss = false;
|
||||
|
||||
System.out.println("Loss a packet of handshake messahe");
|
||||
packets.remove(packet);
|
||||
}
|
||||
}
|
||||
|
||||
return finished;
|
||||
}
|
||||
}
|
114
jdk/test/javax/net/ssl/DTLS/RespondToRetransmit.java
Normal file
114
jdk/test/javax/net/ssl/DTLS/RespondToRetransmit.java
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
// SunJSSE does not support dynamic system properties, no way to re-use
|
||||
// system properties in samevm/agentvm mode.
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8161086
|
||||
* @summary DTLS handshaking fails if some messages were lost
|
||||
* @modules java.base/sun.security.util
|
||||
* @build DTLSOverDatagram
|
||||
*
|
||||
* @run main/othervm RespondToRetransmit client 0 hello_request
|
||||
* @run main/othervm RespondToRetransmit client 1 client_hello
|
||||
* @run main/othervm RespondToRetransmit client 2 server_hello
|
||||
* @run main/othervm RespondToRetransmit client 3 hello_verify_request
|
||||
* @run main/othervm RespondToRetransmit client 4 new_session_ticket
|
||||
* @run main/othervm RespondToRetransmit client 11 certificate
|
||||
* @run main/othervm RespondToRetransmit client 12 server_key_exchange
|
||||
* @run main/othervm RespondToRetransmit client 13 certificate_request
|
||||
* @run main/othervm RespondToRetransmit client 14 server_hello_done
|
||||
* @run main/othervm RespondToRetransmit client 15 certificate_verify
|
||||
* @run main/othervm RespondToRetransmit client 16 client_key_exchange
|
||||
* @run main/othervm RespondToRetransmit client 20 finished
|
||||
* @run main/othervm RespondToRetransmit client 21 certificate_url
|
||||
* @run main/othervm RespondToRetransmit client 22 certificate_status
|
||||
* @run main/othervm RespondToRetransmit client 23 supplemental_data
|
||||
* @run main/othervm RespondToRetransmit client -1 change_cipher_spec
|
||||
* @run main/othervm RespondToRetransmit server 0 hello_request
|
||||
* @run main/othervm RespondToRetransmit server 1 client_hello
|
||||
* @run main/othervm RespondToRetransmit server 2 server_hello
|
||||
* @run main/othervm RespondToRetransmit server 3 hello_verify_request
|
||||
* @run main/othervm RespondToRetransmit server 4 new_session_ticket
|
||||
* @run main/othervm RespondToRetransmit server 11 certificate
|
||||
* @run main/othervm RespondToRetransmit server 12 server_key_exchange
|
||||
* @run main/othervm RespondToRetransmit server 13 certificate_request
|
||||
* @run main/othervm RespondToRetransmit server 14 server_hello_done
|
||||
* @run main/othervm RespondToRetransmit server 15 certificate_verify
|
||||
* @run main/othervm RespondToRetransmit server 16 client_key_exchange
|
||||
* @run main/othervm RespondToRetransmit server 20 finished
|
||||
* @run main/othervm RespondToRetransmit server 21 certificate_url
|
||||
* @run main/othervm RespondToRetransmit server 22 certificate_status
|
||||
* @run main/othervm RespondToRetransmit server 23 supplemental_data
|
||||
* @run main/othervm RespondToRetransmit server -1 change_cipher_spec
|
||||
*/
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ArrayList;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.SocketAddress;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
|
||||
/**
|
||||
* Test that DTLS implementation is able to do retransmission internally
|
||||
* automatically if packet get lost.
|
||||
*/
|
||||
public class RespondToRetransmit extends DTLSOverDatagram {
|
||||
private static boolean isClient;
|
||||
private static byte handshakeType;
|
||||
|
||||
private boolean needPacketDuplicate = true;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
isClient = args[0].equals("client");
|
||||
handshakeType = Byte.valueOf(args[1]);
|
||||
|
||||
RespondToRetransmit testCase = new RespondToRetransmit();
|
||||
testCase.runTest(testCase);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean produceHandshakePackets(SSLEngine engine, SocketAddress socketAddr,
|
||||
String side, List<DatagramPacket> packets) throws Exception {
|
||||
|
||||
boolean finished = super.produceHandshakePackets(
|
||||
engine, socketAddr, side, packets);
|
||||
|
||||
if (needPacketDuplicate && (!(isClient ^ engine.getUseClientMode()))) {
|
||||
DatagramPacket packet = getPacket(packets, handshakeType);
|
||||
if (packet != null) {
|
||||
needPacketDuplicate = false;
|
||||
|
||||
System.out.println("Duplicate the flight.");
|
||||
List<DatagramPacket> duplicates = new ArrayList<>();
|
||||
finished = super.produceHandshakePackets(
|
||||
engine, socketAddr, side, duplicates);
|
||||
packets.addAll(duplicates);
|
||||
}
|
||||
}
|
||||
|
||||
return finished;
|
||||
}
|
||||
}
|
@ -27,7 +27,9 @@ import javax.net.ssl.SNIMatcher;
|
||||
import javax.net.ssl.SNIServerName;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLEngineResult;
|
||||
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
@ -57,19 +59,21 @@ abstract public class SSLEngineTestCase {
|
||||
public enum Ciphers {
|
||||
|
||||
/**
|
||||
* Ciphers supported by the tested SSLEngine without those with kerberos
|
||||
* authentication.
|
||||
* Ciphers supported by the tested SSLEngine without those with
|
||||
* kerberos authentication.
|
||||
*/
|
||||
SUPPORTED_NON_KRB_CIPHERS(SSLEngineTestCase.SUPPORTED_NON_KRB_CIPHERS,
|
||||
"Supported non kerberos"),
|
||||
/**
|
||||
* Ciphers supported by the tested SSLEngine without those with kerberos
|
||||
* authentication and without those with SHA256 ans SHA384.
|
||||
* Ciphers supported by the tested SSLEngine without those with
|
||||
* kerberos authentication and without those with SHA256 ans SHA384.
|
||||
*/
|
||||
SUPPORTED_NON_KRB_NON_SHA_CIPHERS(SSLEngineTestCase.SUPPORTED_NON_KRB_NON_SHA_CIPHERS,
|
||||
SUPPORTED_NON_KRB_NON_SHA_CIPHERS(
|
||||
SSLEngineTestCase.SUPPORTED_NON_KRB_NON_SHA_CIPHERS,
|
||||
"Supported non kerberos non SHA256 and SHA384"),
|
||||
/**
|
||||
* Ciphers supported by the tested SSLEngine with kerberos authentication.
|
||||
* Ciphers supported by the tested SSLEngine with kerberos
|
||||
* authentication.
|
||||
*/
|
||||
SUPPORTED_KRB_CIPHERS(SSLEngineTestCase.SUPPORTED_KRB_CIPHERS,
|
||||
"Supported kerberos"),
|
||||
@ -147,13 +151,13 @@ abstract public class SSLEngineTestCase {
|
||||
= System.getProperty("test.src", ".") + FS + PATH_TO_STORES
|
||||
+ FS + TRUST_STORE_FILE;
|
||||
|
||||
// Need an enhancement to use none-static mutable global variables.
|
||||
private static ByteBuffer net;
|
||||
private static ByteBuffer netReplicatedClient;
|
||||
private static ByteBuffer netReplicatedServer;
|
||||
private static final int MAX_HANDSHAKE_LOOPS = 100;
|
||||
private static final String EXCHANGE_MSG_SENT = "Hello, peer!";
|
||||
private static boolean doUnwrapForNotHandshakingStatus;
|
||||
private static boolean endHandshakeLoop = false;
|
||||
|
||||
private static final int MAX_HANDSHAKE_LOOPS = 100;
|
||||
private static final String EXCHANGE_MSG_SENT = "Hello, peer!";
|
||||
private static final String TEST_SRC = System.getProperty("test.src", ".");
|
||||
private static final String KTAB_FILENAME = "krb5.keytab.data";
|
||||
private static final String KRB_REALM = "TEST.REALM";
|
||||
@ -179,11 +183,13 @@ abstract public class SSLEngineTestCase {
|
||||
List<String> supportedCiphersList = new LinkedList<>();
|
||||
for (String cipher : allSupportedCiphers) {
|
||||
if (!cipher.contains("KRB5")
|
||||
&& !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
|
||||
&& !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
|
||||
|
||||
supportedCiphersList.add(cipher);
|
||||
}
|
||||
}
|
||||
SUPPORTED_NON_KRB_CIPHERS = supportedCiphersList.toArray(new String[0]);
|
||||
SUPPORTED_NON_KRB_CIPHERS =
|
||||
supportedCiphersList.toArray(new String[0]);
|
||||
} catch (Exception ex) {
|
||||
throw new Error("Unexpected issue", ex);
|
||||
}
|
||||
@ -220,7 +226,7 @@ abstract public class SSLEngineTestCase {
|
||||
List<String> supportedCiphersList = new LinkedList<>();
|
||||
for (String cipher : allSupportedCiphers) {
|
||||
if (cipher.contains("KRB5")
|
||||
&& !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
|
||||
&& !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
|
||||
supportedCiphersList.add(cipher);
|
||||
}
|
||||
}
|
||||
@ -240,11 +246,12 @@ abstract public class SSLEngineTestCase {
|
||||
List<String> enabledCiphersList = new LinkedList<>();
|
||||
for (String cipher : enabledCiphers) {
|
||||
if (!cipher.contains("anon") && !cipher.contains("KRB5")
|
||||
&& !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
|
||||
&& !cipher.contains("TLS_EMPTY_RENEGOTIATION_INFO_SCSV")) {
|
||||
enabledCiphersList.add(cipher);
|
||||
}
|
||||
}
|
||||
ENABLED_NON_KRB_NOT_ANON_CIPHERS = enabledCiphersList.toArray(new String[0]);
|
||||
ENABLED_NON_KRB_NOT_ANON_CIPHERS =
|
||||
enabledCiphersList.toArray(new String[0]);
|
||||
} catch (Exception ex) {
|
||||
throw new Error("Unexpected issue", ex);
|
||||
}
|
||||
@ -300,10 +307,10 @@ abstract public class SSLEngineTestCase {
|
||||
* Wraps data with the specified engine.
|
||||
*
|
||||
* @param engine - SSLEngine that wraps data.
|
||||
* @param wrapper - Set wrapper id, e.g. "server" of "client". Used for
|
||||
* logging only.
|
||||
* @param maxPacketSize - Max packet size to check that MFLN extension works
|
||||
* or zero for no check.
|
||||
* @param wrapper - Set wrapper id, e.g. "server" of "client".
|
||||
* Used for logging only.
|
||||
* @param maxPacketSize - Max packet size to check that MFLN extension
|
||||
* works or zero for no check.
|
||||
* @param app - Buffer with data to wrap.
|
||||
* @return - Buffer with wrapped data.
|
||||
* @throws SSLException - thrown on engine errors.
|
||||
@ -319,13 +326,13 @@ abstract public class SSLEngineTestCase {
|
||||
* Wraps data with the specified engine.
|
||||
*
|
||||
* @param engine - SSLEngine that wraps data.
|
||||
* @param wrapper - Set wrapper id, e.g. "server" of "client". Used for
|
||||
* logging only.
|
||||
* @param maxPacketSize - Max packet size to check that MFLN extension works
|
||||
* or zero for no check.
|
||||
* @param wrapper - Set wrapper id, e.g. "server" of "client".
|
||||
* Used for logging only.
|
||||
* @param maxPacketSize - Max packet size to check that MFLN extension
|
||||
* works or zero for no check.
|
||||
* @param app - Buffer with data to wrap.
|
||||
* @param result - Array which first element will be used to output wrap
|
||||
* result object.
|
||||
* @param result - Array which first element will be used to
|
||||
* output wrap result object.
|
||||
* @return - Buffer with wrapped data.
|
||||
* @throws SSLException - thrown on engine errors.
|
||||
*/
|
||||
@ -341,10 +348,10 @@ abstract public class SSLEngineTestCase {
|
||||
* Wraps data with the specified engine.
|
||||
*
|
||||
* @param engine - SSLEngine that wraps data.
|
||||
* @param wrapper - Set wrapper id, e.g. "server" of "client". Used for
|
||||
* logging only.
|
||||
* @param maxPacketSize - Max packet size to check that MFLN extension works
|
||||
* or zero for no check.
|
||||
* @param wrapper - Set wrapper id, e.g. "server" of "client".
|
||||
* Used for logging only.
|
||||
* @param maxPacketSize - Max packet size to check that MFLN extension
|
||||
* works or zero for no check.
|
||||
* @param app - Buffer with data to wrap.
|
||||
* @param wantedStatus - Specifies expected result status of wrapping.
|
||||
* @return - Buffer with wrapped data.
|
||||
@ -362,14 +369,14 @@ abstract public class SSLEngineTestCase {
|
||||
* Wraps data with the specified engine.
|
||||
*
|
||||
* @param engine - SSLEngine that wraps data.
|
||||
* @param wrapper - Set wrapper id, e.g. "server" of "client". Used for
|
||||
* logging only.
|
||||
* @param maxPacketSize - Max packet size to check that MFLN extension works
|
||||
* or zero for no check.
|
||||
* @param wrapper - Set wrapper id, e.g. "server" of "client".
|
||||
* Used for logging only.
|
||||
* @param maxPacketSize - Max packet size to check that MFLN extension
|
||||
* works or zero for no check.
|
||||
* @param app - Buffer with data to wrap.
|
||||
* @param wantedStatus - Specifies expected result status of wrapping.
|
||||
* @param result - Array which first element will be used to output wrap
|
||||
* result object.
|
||||
* @param result - Array which first element will be used to output
|
||||
* wrap result object.
|
||||
* @return - Buffer with wrapped data.
|
||||
* @throws SSLException - thrown on engine errors.
|
||||
*/
|
||||
@ -409,9 +416,9 @@ abstract public class SSLEngineTestCase {
|
||||
* @throws SSLException - thrown on engine errors.
|
||||
*/
|
||||
public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper,
|
||||
ByteBuffer net)
|
||||
throws SSLException {
|
||||
return doUnWrap(engine, unwrapper, net, SSLEngineResult.Status.OK, null);
|
||||
ByteBuffer net) throws SSLException {
|
||||
return doUnWrap(engine, unwrapper,
|
||||
net, SSLEngineResult.Status.OK, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -427,26 +434,25 @@ abstract public class SSLEngineTestCase {
|
||||
* @throws SSLException - thrown on engine errors.
|
||||
*/
|
||||
public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper,
|
||||
ByteBuffer net, SSLEngineResult[] result)
|
||||
throws SSLException {
|
||||
return doUnWrap(engine, unwrapper, net, SSLEngineResult.Status.OK, result);
|
||||
ByteBuffer net, SSLEngineResult[] result) throws SSLException {
|
||||
return doUnWrap(engine, unwrapper,
|
||||
net, SSLEngineResult.Status.OK, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwraps data with the specified engine.
|
||||
*
|
||||
* @param engine - SSLEngine that unwraps data.
|
||||
* @param unwrapper - Set unwrapper id, e.g. "server" of "client". Used for
|
||||
* logging only.
|
||||
* @param unwrapper - Set unwrapper id, e.g. "server" of "client".
|
||||
* Used for logging only.
|
||||
* @param net - Buffer with data to unwrap.
|
||||
* @param wantedStatus - Specifies expected result status of wrapping.
|
||||
* @return - Buffer with unwrapped data.
|
||||
* @throws SSLException - thrown on engine errors.
|
||||
*/
|
||||
public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper,
|
||||
ByteBuffer net,
|
||||
SSLEngineResult.Status wantedStatus)
|
||||
throws SSLException {
|
||||
ByteBuffer net,
|
||||
SSLEngineResult.Status wantedStatus) throws SSLException {
|
||||
return doUnWrap(engine, unwrapper, net, wantedStatus, null);
|
||||
}
|
||||
|
||||
@ -454,25 +460,23 @@ abstract public class SSLEngineTestCase {
|
||||
* Unwraps data with the specified engine.
|
||||
*
|
||||
* @param engine - SSLEngine that unwraps data.
|
||||
* @param unwrapper - Set unwrapper id, e.g. "server" of "client". Used for
|
||||
* logging only.
|
||||
* @param unwrapper - Set unwrapper id, e.g. "server" of "client".
|
||||
* Used for logging only.
|
||||
* @param net - Buffer with data to unwrap.
|
||||
* @param wantedStatus - Specifies expected result status of wrapping.
|
||||
* @param result - Array which first element will be used to output wrap
|
||||
* result object.
|
||||
* @param result - Array which first element will be used to output
|
||||
* wrap result object.
|
||||
* @return - Buffer with unwrapped data.
|
||||
* @throws SSLException - thrown on engine errors.
|
||||
*/
|
||||
public static ByteBuffer doUnWrap(SSLEngine engine, String unwrapper,
|
||||
ByteBuffer net,
|
||||
SSLEngineResult.Status wantedStatus,
|
||||
SSLEngineResult[] result)
|
||||
throws SSLException {
|
||||
ByteBuffer app = ByteBuffer.allocate(engine.getSession()
|
||||
.getApplicationBufferSize());
|
||||
ByteBuffer net, SSLEngineResult.Status wantedStatus,
|
||||
SSLEngineResult[] result) throws SSLException {
|
||||
|
||||
ByteBuffer app = ByteBuffer.allocate(
|
||||
engine.getSession().getApplicationBufferSize());
|
||||
int length = net.remaining();
|
||||
System.out.println(unwrapper + " unwrapping "
|
||||
+ length + " bytes...");
|
||||
System.out.println(unwrapper + " unwrapping " + length + " bytes...");
|
||||
SSLEngineResult r = engine.unwrap(net, app);
|
||||
app.flip();
|
||||
System.out.println(unwrapper + " handshake status is "
|
||||
@ -491,13 +495,14 @@ abstract public class SSLEngineTestCase {
|
||||
* @param clientEngine - Client SSLEngine.
|
||||
* @param serverEngine - Server SSLEngine.
|
||||
* @param maxPacketSize - Maximum packet size for MFLN of zero for no limit.
|
||||
* @param mode - Handshake mode according to {@link HandshakeMode} enum.
|
||||
* @param mode - Handshake mode according to
|
||||
* {@link HandshakeMode} enum.
|
||||
* @throws SSLException - thrown on engine errors.
|
||||
*/
|
||||
public static void doHandshake(SSLEngine clientEngine,
|
||||
SSLEngine serverEngine,
|
||||
int maxPacketSize, HandshakeMode mode)
|
||||
throws SSLException {
|
||||
SSLEngine serverEngine,
|
||||
int maxPacketSize, HandshakeMode mode) throws SSLException {
|
||||
|
||||
doHandshake(clientEngine, serverEngine, maxPacketSize, mode, false);
|
||||
}
|
||||
|
||||
@ -507,19 +512,20 @@ abstract public class SSLEngineTestCase {
|
||||
*
|
||||
* @param clientEngine - Client SSLEngine.
|
||||
* @param serverEngine - Server SSLEngine.
|
||||
* @param maxPacketSize - Maximum packet size for MFLN of zero for no limit.
|
||||
* @param mode - Handshake mode according to {@link HandshakeMode} enum.
|
||||
* @param maxPacketSize - Maximum packet size for MFLN of zero
|
||||
* for no limit.
|
||||
* @param mode - Handshake mode according to
|
||||
* {@link HandshakeMode} enum.
|
||||
* @param enableReplicatedPacks - Set {@code true} to enable replicated
|
||||
* packet sending.
|
||||
* packet sending.
|
||||
* @throws SSLException - thrown on engine errors.
|
||||
*/
|
||||
public static void doHandshake(SSLEngine clientEngine,
|
||||
SSLEngine serverEngine, int maxPacketSize,
|
||||
HandshakeMode mode,
|
||||
boolean enableReplicatedPacks)
|
||||
throws SSLException {
|
||||
System.out.println("================================================="
|
||||
+ "===========");
|
||||
SSLEngine serverEngine, int maxPacketSize,
|
||||
HandshakeMode mode,
|
||||
boolean enableReplicatedPacks) throws SSLException {
|
||||
|
||||
System.out.println("=============================================");
|
||||
System.out.println("Starting handshake " + mode.name());
|
||||
int loop = 0;
|
||||
if (maxPacketSize < 0) {
|
||||
@ -561,18 +567,16 @@ abstract public class SSLEngineTestCase {
|
||||
if (++loop > MAX_HANDSHAKE_LOOPS) {
|
||||
throw new Error("Too much loops for handshaking");
|
||||
}
|
||||
System.out.println("==============================================");
|
||||
System.out.println("Handshake loop " + loop);
|
||||
SSLEngineResult.HandshakeStatus clientHSStatus
|
||||
= clientEngine.getHandshakeStatus();
|
||||
SSLEngineResult.HandshakeStatus serverHSStatus
|
||||
= serverEngine.getHandshakeStatus();
|
||||
System.out.println("Client handshake status "
|
||||
+ clientHSStatus.name());
|
||||
System.out.println("Server handshake status "
|
||||
+ serverHSStatus.name());
|
||||
System.out.println("============================================");
|
||||
System.out.println("Handshake loop " + loop + ": round 1");
|
||||
System.out.println("==========================");
|
||||
handshakeProcess(firstEngine, secondEngine, maxPacketSize,
|
||||
enableReplicatedPacks);
|
||||
if (endHandshakeLoop) {
|
||||
break;
|
||||
}
|
||||
System.out.println("Handshake loop " + loop + ": round 2");
|
||||
System.out.println("==========================");
|
||||
handshakeProcess(secondEngine, firstEngine, maxPacketSize,
|
||||
enableReplicatedPacks);
|
||||
}
|
||||
@ -596,15 +600,15 @@ abstract public class SSLEngineTestCase {
|
||||
sender = "Client";
|
||||
reciever = "Server";
|
||||
excMsgSent += " Client.";
|
||||
} else if (toEngine.getUseClientMode() && !fromEngine.getUseClientMode()) {
|
||||
} else if (toEngine.getUseClientMode() &&
|
||||
!fromEngine.getUseClientMode()) {
|
||||
sender = "Server";
|
||||
reciever = "Client";
|
||||
excMsgSent += " Server.";
|
||||
} else {
|
||||
throw new Error("Test issue: both engines are in the same mode");
|
||||
}
|
||||
System.out.println("================================================="
|
||||
+ "===========");
|
||||
System.out.println("=============================================");
|
||||
System.out.println("Trying to send application data from " + sender
|
||||
+ " to " + reciever);
|
||||
ByteBuffer clientAppSent
|
||||
@ -643,20 +647,24 @@ abstract public class SSLEngineTestCase {
|
||||
if (fromEngine.getUseClientMode() && !toEngine.getUseClientMode()) {
|
||||
from = "Client";
|
||||
to = "Server";
|
||||
} else if (toEngine.getUseClientMode() && !fromEngine.getUseClientMode()) {
|
||||
} else if (toEngine.getUseClientMode() &&
|
||||
!fromEngine.getUseClientMode()) {
|
||||
from = "Server";
|
||||
to = "Client";
|
||||
} else {
|
||||
throw new Error("Both engines are in the same mode");
|
||||
}
|
||||
System.out.println("=========================================================");
|
||||
System.out.println("Trying to close engines from " + from + " to " + to);
|
||||
System.out.println("=============================================");
|
||||
System.out.println(
|
||||
"Trying to close engines from " + from + " to " + to);
|
||||
// Sending close outbound request to peer
|
||||
fromEngine.closeOutbound();
|
||||
app = ByteBuffer.allocate(fromEngine.getSession().getApplicationBufferSize());
|
||||
app = ByteBuffer.allocate(
|
||||
fromEngine.getSession().getApplicationBufferSize());
|
||||
net = doWrap(fromEngine, from, 0, app, SSLEngineResult.Status.CLOSED);
|
||||
doUnWrap(toEngine, to, net, SSLEngineResult.Status.CLOSED);
|
||||
app = ByteBuffer.allocate(fromEngine.getSession().getApplicationBufferSize());
|
||||
app = ByteBuffer.allocate(
|
||||
fromEngine.getSession().getApplicationBufferSize());
|
||||
net = doWrap(toEngine, to, 0, app, SSLEngineResult.Status.CLOSED);
|
||||
doUnWrap(fromEngine, from, net, SSLEngineResult.Status.CLOSED);
|
||||
if (!toEngine.isInboundDone()) {
|
||||
@ -665,7 +673,8 @@ abstract public class SSLEngineTestCase {
|
||||
}
|
||||
// Executing close inbound
|
||||
fromEngine.closeInbound();
|
||||
app = ByteBuffer.allocate(fromEngine.getSession().getApplicationBufferSize());
|
||||
app = ByteBuffer.allocate(
|
||||
fromEngine.getSession().getApplicationBufferSize());
|
||||
net = doWrap(fromEngine, from, 0, app, SSLEngineResult.Status.CLOSED);
|
||||
doUnWrap(toEngine, to, net, SSLEngineResult.Status.CLOSED);
|
||||
if (!toEngine.isOutboundDone()) {
|
||||
@ -712,7 +721,8 @@ abstract public class SSLEngineTestCase {
|
||||
runTests(Ciphers.SUPPORTED_KRB_CIPHERS);
|
||||
break;
|
||||
default:
|
||||
throw new Error("Test error: unexpected test mode: " + TEST_MODE);
|
||||
throw new Error(
|
||||
"Test error: unexpected test mode: " + TEST_MODE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -743,28 +753,36 @@ abstract public class SSLEngineTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns SSLContext with TESTED_SECURITY_PROTOCOL protocol and sets up keys.
|
||||
* Returns SSLContext with TESTED_SECURITY_PROTOCOL protocol and
|
||||
* sets up keys.
|
||||
*
|
||||
* @return - SSLContext with a protocol specified by TESTED_SECURITY_PROTOCOL.
|
||||
* @return - SSLContext with a protocol specified by
|
||||
* TESTED_SECURITY_PROTOCOL.
|
||||
*/
|
||||
public static SSLContext getContext() {
|
||||
try {
|
||||
java.security.Security.setProperty("jdk.tls.disabledAlgorithms", "");
|
||||
java.security.Security.setProperty("jdk.certpath.disabledAlgorithms", "");
|
||||
java.security.Security.setProperty(
|
||||
"jdk.tls.disabledAlgorithms", "");
|
||||
java.security.Security.setProperty(
|
||||
"jdk.certpath.disabledAlgorithms", "");
|
||||
KeyStore ks = KeyStore.getInstance("JKS");
|
||||
KeyStore ts = KeyStore.getInstance("JKS");
|
||||
char[] passphrase = PASSWD.toCharArray();
|
||||
try (FileInputStream keyFileStream = new FileInputStream(KEY_FILE_NAME)) {
|
||||
try (FileInputStream keyFileStream =
|
||||
new FileInputStream(KEY_FILE_NAME)) {
|
||||
ks.load(keyFileStream, passphrase);
|
||||
}
|
||||
try (FileInputStream trustFileStream = new FileInputStream(TRUST_FILE_NAME)) {
|
||||
try (FileInputStream trustFileStream =
|
||||
new FileInputStream(TRUST_FILE_NAME)) {
|
||||
ts.load(trustFileStream, passphrase);
|
||||
}
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
|
||||
kmf.init(ks, passphrase);
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
|
||||
TrustManagerFactory tmf =
|
||||
TrustManagerFactory.getInstance("SunX509");
|
||||
tmf.init(ts);
|
||||
SSLContext sslCtx = SSLContext.getInstance(TESTED_SECURITY_PROTOCOL);
|
||||
SSLContext sslCtx =
|
||||
SSLContext.getInstance(TESTED_SECURITY_PROTOCOL);
|
||||
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||
return sslCtx;
|
||||
} catch (KeyStoreException | IOException | NoSuchAlgorithmException |
|
||||
@ -791,7 +809,8 @@ abstract public class SSLEngineTestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up and starts kerberos KDC server if SSLEngineTestCase.TEST_MODE is "krb".
|
||||
* Sets up and starts kerberos KDC server if
|
||||
* SSLEngineTestCase.TEST_MODE is "krb".
|
||||
*/
|
||||
public static void setUpAndStartKDCIfNeeded() {
|
||||
if (TEST_MODE.equals("krb")) {
|
||||
@ -806,7 +825,9 @@ abstract public class SSLEngineTestCase {
|
||||
* @param useSNI - flag used to enable or disable using SNI extension.
|
||||
* Needed for Kerberos.
|
||||
*/
|
||||
public static SSLEngine getClientSSLEngine(SSLContext context, boolean useSNI) {
|
||||
public static SSLEngine getClientSSLEngine(
|
||||
SSLContext context, boolean useSNI) {
|
||||
|
||||
SSLEngine clientEngine = context.createSSLEngine(HOST, 80);
|
||||
clientEngine.setUseClientMode(true);
|
||||
if (useSNI) {
|
||||
@ -827,7 +848,9 @@ abstract public class SSLEngineTestCase {
|
||||
* @param useSNI - flag used to enable or disable using SNI extension.
|
||||
* Needed for Kerberos.
|
||||
*/
|
||||
public static SSLEngine getServerSSLEngine(SSLContext context, boolean useSNI) {
|
||||
public static SSLEngine getServerSSLEngine(
|
||||
SSLContext context, boolean useSNI) {
|
||||
|
||||
SSLEngine serverEngine = context.createSSLEngine();
|
||||
serverEngine.setUseClientMode(false);
|
||||
if (useSNI) {
|
||||
@ -860,18 +883,20 @@ abstract public class SSLEngineTestCase {
|
||||
protected int testSomeCiphers(Ciphers ciphers) {
|
||||
int failedNum = 0;
|
||||
String description = ciphers.description;
|
||||
System.out.println("==================================================="
|
||||
+ "=========");
|
||||
System.out.println("===============================================");
|
||||
System.out.println(description + " ciphers testing");
|
||||
System.out.println("==================================================="
|
||||
+ "=========");
|
||||
System.out.println("===========================================");
|
||||
for (String cs : ciphers.ciphers) {
|
||||
System.out.println("-----------------------------------------------"
|
||||
+ "-------------");
|
||||
System.out.println("---------------------------------------");
|
||||
System.out.println("Testing cipher suite " + cs);
|
||||
System.out.println("-----------------------------------------------"
|
||||
+ "-------------");
|
||||
System.out.println("---------------------------------------");
|
||||
Throwable error = null;
|
||||
|
||||
// Reset global mutable static variables
|
||||
net = null;
|
||||
doUnwrapForNotHandshakingStatus = false;
|
||||
endHandshakeLoop = false;
|
||||
|
||||
try {
|
||||
testOneCipher(cs);
|
||||
} catch (Throwable t) {
|
||||
@ -894,8 +919,9 @@ abstract public class SSLEngineTestCase {
|
||||
case UNSUPPORTED_CIPHERS:
|
||||
if (error == null) {
|
||||
System.out.println("Test Failed: " + cs);
|
||||
System.err.println("Test for " + cs + " should have thrown"
|
||||
+ " IllegalArgumentException, but it has not!");
|
||||
System.err.println("Test for " + cs +
|
||||
" should have thrown " +
|
||||
"IllegalArgumentException, but it has not!");
|
||||
failedNum++;
|
||||
} else if (!(error instanceof IllegalArgumentException)) {
|
||||
System.out.println("Test Failed: " + cs);
|
||||
@ -911,6 +937,7 @@ abstract public class SSLEngineTestCase {
|
||||
+ ciphers.name());
|
||||
}
|
||||
}
|
||||
|
||||
return failedNum;
|
||||
}
|
||||
|
||||
@ -919,20 +946,20 @@ abstract public class SSLEngineTestCase {
|
||||
*
|
||||
* @param wrapingEngine - Engine that is expected to wrap data.
|
||||
* @param unwrapingEngine - Engine that is expected to unwrap data.
|
||||
* @param maxPacketSize - Maximum packet size for MFLN of zero for no limit.
|
||||
* @param maxPacketSize - Maximum packet size for MFLN of zero
|
||||
* for no limit.
|
||||
* @param enableReplicatedPacks - Set {@code true} to enable replicated
|
||||
* packet sending.
|
||||
* packet sending.
|
||||
* @throws SSLException - thrown on engine errors.
|
||||
*/
|
||||
private static void handshakeProcess(SSLEngine wrapingEngine,
|
||||
SSLEngine unwrapingEngine,
|
||||
int maxPacketSize,
|
||||
boolean enableReplicatedPacks)
|
||||
throws SSLException {
|
||||
SSLEngineResult.HandshakeStatus wrapingHSStatus = wrapingEngine
|
||||
.getHandshakeStatus();
|
||||
SSLEngineResult.HandshakeStatus unwrapingHSStatus = unwrapingEngine
|
||||
.getHandshakeStatus();
|
||||
SSLEngine unwrapingEngine,
|
||||
int maxPacketSize,
|
||||
boolean enableReplicatedPacks) throws SSLException {
|
||||
|
||||
HandshakeStatus wrapingHSStatus = wrapingEngine.getHandshakeStatus();
|
||||
HandshakeStatus unwrapingHSStatus =
|
||||
unwrapingEngine.getHandshakeStatus();
|
||||
SSLEngineResult r;
|
||||
String wrapper, unwrapper;
|
||||
if (wrapingEngine.getUseClientMode()
|
||||
@ -946,6 +973,13 @@ abstract public class SSLEngineTestCase {
|
||||
} else {
|
||||
throw new Error("Both engines are in the same mode");
|
||||
}
|
||||
System.out.println(
|
||||
wrapper + " handshake (wrap) status " + wrapingHSStatus);
|
||||
System.out.println(
|
||||
unwrapper + " handshake (unwrap) status " + unwrapingHSStatus);
|
||||
|
||||
ByteBuffer netReplicatedClient = null;
|
||||
ByteBuffer netReplicatedServer = null;
|
||||
switch (wrapingHSStatus) {
|
||||
case NEED_WRAP:
|
||||
if (enableReplicatedPacks) {
|
||||
@ -960,9 +994,11 @@ abstract public class SSLEngineTestCase {
|
||||
}
|
||||
}
|
||||
}
|
||||
ByteBuffer app = ByteBuffer.allocate(wrapingEngine.getSession()
|
||||
.getApplicationBufferSize());
|
||||
ByteBuffer app = ByteBuffer.allocate(
|
||||
wrapingEngine.getSession().getApplicationBufferSize());
|
||||
net = doWrap(wrapingEngine, wrapper, maxPacketSize, app);
|
||||
wrapingHSStatus = wrapingEngine.getHandshakeStatus();
|
||||
// No break, falling into unwrapping.
|
||||
case NOT_HANDSHAKING:
|
||||
switch (unwrapingHSStatus) {
|
||||
case NEED_TASK:
|
||||
@ -970,12 +1006,12 @@ abstract public class SSLEngineTestCase {
|
||||
case NEED_UNWRAP:
|
||||
doUnWrap(unwrapingEngine, unwrapper, net);
|
||||
if (enableReplicatedPacks) {
|
||||
System.out.println("Unwrapping replicated packet...");
|
||||
System.out.println(unwrapper +
|
||||
" unwrapping replicated packet...");
|
||||
if (unwrapingEngine.getHandshakeStatus()
|
||||
.equals(SSLEngineResult.HandshakeStatus.NEED_TASK)) {
|
||||
.equals(HandshakeStatus.NEED_TASK)) {
|
||||
runDelegatedTasks(unwrapingEngine);
|
||||
}
|
||||
runDelegatedTasks(unwrapingEngine);
|
||||
ByteBuffer netReplicated;
|
||||
if (unwrapingEngine.getUseClientMode()) {
|
||||
netReplicated = netReplicatedClient;
|
||||
@ -983,7 +1019,8 @@ abstract public class SSLEngineTestCase {
|
||||
netReplicated = netReplicatedServer;
|
||||
}
|
||||
if (netReplicated != null) {
|
||||
doUnWrap(unwrapingEngine, unwrapper, netReplicated);
|
||||
doUnWrap(unwrapingEngine,
|
||||
unwrapper, netReplicated);
|
||||
} else {
|
||||
net.flip();
|
||||
doUnWrap(unwrapingEngine, unwrapper, net);
|
||||
@ -994,15 +1031,39 @@ abstract public class SSLEngineTestCase {
|
||||
break;
|
||||
case NOT_HANDSHAKING:
|
||||
if (doUnwrapForNotHandshakingStatus) {
|
||||
System.out.println("Not handshake status unwrap");
|
||||
doUnWrap(unwrapingEngine, unwrapper, net);
|
||||
doUnwrapForNotHandshakingStatus = false;
|
||||
break;
|
||||
} else {
|
||||
endHandshakeLoop = true;
|
||||
if (wrapingHSStatus ==
|
||||
HandshakeStatus.NOT_HANDSHAKING) {
|
||||
System.out.println("Handshake is completed");
|
||||
endHandshakeLoop = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NEED_WRAP:
|
||||
SSLSession session = unwrapingEngine.getSession();
|
||||
int bufferSize = session.getApplicationBufferSize();
|
||||
ByteBuffer b = ByteBuffer.allocate(bufferSize);
|
||||
net = doWrap(unwrapingEngine,
|
||||
unwrapper, maxPacketSize, b);
|
||||
unwrapingHSStatus =
|
||||
unwrapingEngine.getHandshakeStatus();
|
||||
if ((wrapingHSStatus ==
|
||||
HandshakeStatus.NOT_HANDSHAKING) &&
|
||||
(unwrapingHSStatus ==
|
||||
HandshakeStatus.NOT_HANDSHAKING)) {
|
||||
|
||||
System.out.println("Handshake is completed");
|
||||
endHandshakeLoop = true;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unexpected unwraping engine handshake status "
|
||||
throw new Error(
|
||||
"Unexpected unwraping engine handshake status "
|
||||
+ unwrapingHSStatus.name());
|
||||
}
|
||||
break;
|
||||
@ -1027,8 +1088,8 @@ abstract public class SSLEngineTestCase {
|
||||
while ((runnable = engine.getDelegatedTask()) != null) {
|
||||
runnable.run();
|
||||
}
|
||||
SSLEngineResult.HandshakeStatus hs = engine.getHandshakeStatus();
|
||||
if (hs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
|
||||
HandshakeStatus hs = engine.getHandshakeStatus();
|
||||
if (hs == HandshakeStatus.NEED_TASK) {
|
||||
throw new Error("Handshake shouldn't need additional tasks.");
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user