Authors: russell@unturf.com · brackishbert@gmail.com · foxhop.net · TimeHexOn.com Patches, unit tests, benchmarks, whitepaper, and outreach briefs. Public domain — no copyright claimed. Use freely.
573 lines
23 KiB
Java
573 lines
23 KiB
Java
/*
|
|
* Copyright (c) 2025, 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 8341346
|
|
* @summary Add support for exporting TLS Keying Material
|
|
* @library /javax/net/ssl/templates /test/lib /test/jdk/sun/security/pkcs11
|
|
* @build SSLEngineTemplate
|
|
* @run main/othervm ExportKeyingMaterialTests
|
|
* @run main/othervm ExportKeyingMaterialTests PKCS11
|
|
*/
|
|
|
|
import java.security.Security;
|
|
import java.util.Arrays;
|
|
import javax.crypto.SecretKey;
|
|
import javax.net.ssl.*;
|
|
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
|
import java.nio.ByteBuffer;
|
|
import java.util.Random;
|
|
|
|
import static jdk.test.lib.Asserts.*;
|
|
|
|
/**
|
|
* A SSLEngine usage example which simplifies the presentation
|
|
* by removing the I/O and multi-threading concerns.
|
|
* <P>
|
|
* The test creates two SSLEngines, simulating a client and server.
|
|
* The "transport" layer consists two byte buffers: think of them
|
|
* as directly connected pipes.
|
|
* <P>
|
|
* Note, this is a *very* simple example: real code will be much more
|
|
* involved. For example, different threading and I/O models could be
|
|
* used, transport mechanisms could close unexpectedly, and so on.
|
|
* <P>
|
|
* When this application runs, notice that several messages
|
|
* (wrap/unwrap) pass before any application data is consumed or
|
|
* produced.
|
|
*/
|
|
public class ExportKeyingMaterialTests extends SSLEngineTemplate {
|
|
|
|
private String protocol;
|
|
private String ciphersuite;
|
|
|
|
protected ExportKeyingMaterialTests(String protocol, String ciphersuite)
|
|
throws Exception {
|
|
super();
|
|
this.protocol = protocol;
|
|
this.ciphersuite = ciphersuite;
|
|
}
|
|
|
|
/*
|
|
* Configure the engines.
|
|
*/
|
|
private void configureEngines(SSLEngine clientEngine,
|
|
SSLEngine serverEngine) {
|
|
|
|
clientEngine.setUseClientMode(true);
|
|
|
|
// Get/set parameters if needed
|
|
SSLParameters paramsClient = clientEngine.getSSLParameters();
|
|
paramsClient.setProtocols(new String[] { protocol });
|
|
paramsClient.setCipherSuites(new String[] { ciphersuite });
|
|
clientEngine.setSSLParameters(paramsClient);
|
|
|
|
serverEngine.setUseClientMode(false);
|
|
serverEngine.setNeedClientAuth(true);
|
|
|
|
// Get/set parameters if needed
|
|
//
|
|
SSLParameters paramsServer = serverEngine.getSSLParameters();
|
|
paramsServer.setProtocols(new String[] {
|
|
"TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3"
|
|
});
|
|
serverEngine.setSSLParameters(paramsServer);
|
|
}
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
// Turn off the disabled Algorithms so we can also test SSLv3/TLSv1/etc.
|
|
Security.setProperty("jdk.tls.disabledAlgorithms", "");
|
|
|
|
if ((args.length > 0) && (args[0].equals("PKCS11"))) {
|
|
Security.insertProviderAt(
|
|
PKCS11Test.getSunPKCS11(PKCS11Test.getNssConfig()), 1);
|
|
}
|
|
|
|
// Exercise all of the triggers which capture data
|
|
// in the various key exchange algorithms.
|
|
|
|
// Use appropriate protocol/ciphersuite combos for TLSv1.3
|
|
new ExportKeyingMaterialTests(
|
|
"TLSv1.3", "TLS_AES_128_GCM_SHA256").runTest();
|
|
new ExportKeyingMaterialTests(
|
|
"TLSv1.3", "TLS_AES_256_GCM_SHA384").runTest();
|
|
new ExportKeyingMaterialTests(
|
|
"TLSv1.3", "TLS_CHACHA20_POLY1305_SHA256").runTest();
|
|
|
|
// Try the various GCM suites for TLSv1.2
|
|
new ExportKeyingMaterialTests(
|
|
"TLSv1.2", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384").runTest();
|
|
new ExportKeyingMaterialTests(
|
|
"TLSv1.2", "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256").runTest();
|
|
new ExportKeyingMaterialTests(
|
|
"TLSv1.2", "TLS_RSA_WITH_AES_256_GCM_SHA384").runTest();
|
|
|
|
// Try one TLSv1.2/CBC suite just for grins, the triggers are the same.
|
|
new ExportKeyingMaterialTests(
|
|
"TLSv1.2", "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256").runTest();
|
|
|
|
// Use appropriate protocol/ciphersuite combos. Some of the 1.2
|
|
// suites (e.g. GCM) can't be used in earlier TLS versions.
|
|
new ExportKeyingMaterialTests(
|
|
"TLSv1.1", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA").runTest();
|
|
new ExportKeyingMaterialTests(
|
|
"TLSv1.1", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA").runTest();
|
|
new ExportKeyingMaterialTests(
|
|
"TLSv1.1", "TLS_RSA_WITH_AES_256_CBC_SHA").runTest();
|
|
|
|
new ExportKeyingMaterialTests(
|
|
"TLSv1", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA").runTest();
|
|
new ExportKeyingMaterialTests(
|
|
"TLSv1", "TLS_DHE_RSA_WITH_AES_256_CBC_SHA").runTest();
|
|
new ExportKeyingMaterialTests(
|
|
"TLSv1", "TLS_RSA_WITH_AES_256_CBC_SHA").runTest();
|
|
|
|
try {
|
|
new ExportKeyingMaterialTests(
|
|
"SSLv3", "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA").runTest();
|
|
throw new Exception("SSLv3 export test should not have passed");
|
|
} catch (SSLException e) {
|
|
System.out.println("SSLv3 test failed as expected");
|
|
}
|
|
|
|
System.out.println("All tests PASSED");
|
|
}
|
|
|
|
//
|
|
// Private methods that used to build the common part of the test.
|
|
//
|
|
|
|
private void runTest() throws Exception {
|
|
SSLEngineResult clientResult;
|
|
SSLEngineResult serverResult;
|
|
|
|
configureEngines(clientEngine, serverEngine);
|
|
|
|
boolean dataDone = false;
|
|
while (isOpen(clientEngine) || isOpen(serverEngine)) {
|
|
log("=================");
|
|
|
|
// client wrap
|
|
log("---Client Wrap---");
|
|
clientResult = clientEngine.wrap(clientOut, cTOs);
|
|
logEngineStatus(clientEngine, clientResult);
|
|
runDelegatedTasks(clientEngine);
|
|
|
|
// server wrap
|
|
log("---Server Wrap---");
|
|
serverResult = serverEngine.wrap(serverOut, sTOc);
|
|
logEngineStatus(serverEngine, serverResult);
|
|
runDelegatedTasks(serverEngine);
|
|
|
|
cTOs.flip();
|
|
sTOc.flip();
|
|
|
|
// client unwrap
|
|
log("---Client Unwrap---");
|
|
clientResult = clientEngine.unwrap(sTOc, clientIn);
|
|
logEngineStatus(clientEngine, clientResult);
|
|
runDelegatedTasks(clientEngine);
|
|
|
|
// server unwrap
|
|
log("---Server Unwrap---");
|
|
serverResult = serverEngine.unwrap(cTOs, serverIn);
|
|
logEngineStatus(serverEngine, serverResult);
|
|
runDelegatedTasks(serverEngine);
|
|
|
|
cTOs.compact();
|
|
sTOc.compact();
|
|
|
|
// After we've transferred all application data between the client
|
|
// and server, we close the clientEngine's outbound stream.
|
|
// This generates a close_notify handshake message, which the
|
|
// server engine receives and responds by closing itself.
|
|
if (!dataDone && (clientOut.limit() == serverIn.position()) &&
|
|
(serverOut.limit() == clientIn.position())) {
|
|
|
|
runExporterTests(
|
|
(ExtendedSSLSession) clientEngine.getSession(),
|
|
(ExtendedSSLSession) serverEngine.getSession());
|
|
|
|
// A sanity check to ensure we got what was sent.
|
|
checkTransfer(serverOut, clientIn);
|
|
checkTransfer(clientOut, serverIn);
|
|
|
|
log("\tClosing clientEngine's *OUTBOUND*...");
|
|
clientEngine.closeOutbound();
|
|
logEngineStatus(clientEngine);
|
|
|
|
dataDone = true;
|
|
log("\tClosing serverEngine's *OUTBOUND*...");
|
|
serverEngine.closeOutbound();
|
|
logEngineStatus(serverEngine);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void runExporterTests(
|
|
ExtendedSSLSession clientSession,
|
|
ExtendedSSLSession serverSession) throws Exception {
|
|
|
|
SecretKey clientKey, serverKey;
|
|
|
|
// Create output arrays
|
|
byte[] clientBytes, serverBytes;
|
|
|
|
// Create various input arrays and fill with junk.
|
|
Random random = new Random();
|
|
byte[] bytes = new byte[20];
|
|
random.nextBytes(bytes);
|
|
|
|
// Slightly change 1 byte in the middle
|
|
byte[] bytesDiff = Arrays.copyOf(bytes, bytes.length);
|
|
bytesDiff[bytes.length/2]++;
|
|
|
|
byte[] bytesDiffSize = new byte[21];
|
|
random.nextBytes(bytesDiffSize);
|
|
|
|
// Run a bunch of similar derivations using both the Key/Data methods,
|
|
// exercising the various valid/invalid combinations.
|
|
|
|
// We may need to adjust if it turns out that this is run with
|
|
// non-extractable keys if .equals() doesn't work.
|
|
|
|
// Inputs exactly equal.
|
|
clientKey = clientSession.exportKeyingMaterialKey(
|
|
"Generic", "hello", bytes, 128);
|
|
serverKey = serverSession.exportKeyingMaterialKey(
|
|
"Generic", "hello", bytes, 128);
|
|
assertEquals(clientKey, serverKey,
|
|
"Key: Equal inputs but exporters are not equal");
|
|
log("Key: Equal inputs test passed");
|
|
|
|
clientBytes = clientSession.exportKeyingMaterialData("hello",
|
|
bytes, 128);
|
|
serverBytes = serverSession.exportKeyingMaterialData("hello",
|
|
bytes, 128);
|
|
assertEqualsByteArray(clientBytes, serverBytes,
|
|
"Data: Equal inputs but exporters are not equal");
|
|
log("Data: Equal inputs test passed");
|
|
|
|
// Different labels, now use exportKeyingMaterialData() for coverage
|
|
clientKey = clientSession.exportKeyingMaterialKey(
|
|
"Generic", "hello", bytes, 128);
|
|
serverKey = serverSession.exportKeyingMaterialKey(
|
|
"Generic", "goodbye", bytes, 128);
|
|
assertNotEquals(clientKey, serverKey,
|
|
"Key: Different labels but exporters same");
|
|
log("Key: Different labels test passed");
|
|
|
|
clientBytes = clientSession.exportKeyingMaterialData("hello",
|
|
bytes, 128);
|
|
serverBytes = serverSession.exportKeyingMaterialData("goodbye",
|
|
bytes, 128);
|
|
assertNotEqualsByteArray(clientBytes, serverBytes,
|
|
"Data: Different labels but exporters same");
|
|
log("Data: Different labels test passed");
|
|
|
|
// Different output sizes
|
|
clientKey = clientSession.exportKeyingMaterialKey(
|
|
"Generic", "hello", bytes, 128);
|
|
serverKey = serverSession.exportKeyingMaterialKey(
|
|
"Generic", "hello", bytes, 127);
|
|
assertNotEquals(clientKey, serverKey,
|
|
"Key: Different output sizes but exporters same");
|
|
log("Key: Different output size test passed");
|
|
|
|
clientBytes = clientSession.exportKeyingMaterialData("hello",
|
|
bytes, 128);
|
|
serverBytes = serverSession.exportKeyingMaterialData("hello",
|
|
bytes, 127);
|
|
assertEquals(clientBytes.length, 128, "client length != 128");
|
|
assertEquals(serverBytes.length, 127, "server length != 127");
|
|
assertNotEqualsByteArray(clientBytes, serverBytes,
|
|
"Data: Different output sizes but exporters same");
|
|
log("Data: Different output size test passed");
|
|
|
|
// Different context values
|
|
clientKey = clientSession.exportKeyingMaterialKey(
|
|
"Generic", "hello", bytes, 128);
|
|
serverKey = serverSession.exportKeyingMaterialKey(
|
|
"Generic", "hello", bytesDiff, 128);
|
|
assertNotEquals(clientKey, serverKey,
|
|
"Key: Different context but exporters same");
|
|
log("Key: Different context test passed");
|
|
|
|
clientBytes = clientSession.exportKeyingMaterialData("hello",
|
|
bytes, 128);
|
|
serverBytes = serverSession.exportKeyingMaterialData("hello",
|
|
bytesDiff, 128);
|
|
assertNotEqualsByteArray(clientBytes, serverBytes,
|
|
"Data: Different context but exporters same");
|
|
log("Data: Different context test passed");
|
|
|
|
// Different context sizes
|
|
clientKey = clientSession.exportKeyingMaterialKey(
|
|
"Generic", "hello", bytes, 128);
|
|
serverKey = serverSession.exportKeyingMaterialKey(
|
|
"Generic", "hello", bytesDiffSize, 128);
|
|
assertNotEquals(clientKey, serverKey,
|
|
"Key: Different context sizes but exporters same");
|
|
log("Key: Different context sizes test passed");
|
|
|
|
clientBytes = clientSession.exportKeyingMaterialData("hello",
|
|
bytes, 128);
|
|
serverBytes = serverSession.exportKeyingMaterialData("hello",
|
|
bytesDiffSize, 128);
|
|
assertNotEqualsByteArray(clientBytes, serverBytes,
|
|
"Data: Different context sizes but exporters same");
|
|
log("Data: Different context sizes test passed");
|
|
|
|
// No context, but otherwise the same
|
|
clientKey = clientSession.exportKeyingMaterialKey(
|
|
"Generic", "hello", null, 128);
|
|
serverKey = serverSession.exportKeyingMaterialKey(
|
|
"Generic", "hello", null, 128);
|
|
assertEquals(clientKey, serverKey,
|
|
"Key: No context and exporters are not the same");
|
|
log("Key: No context test passed");
|
|
|
|
clientBytes = clientSession.exportKeyingMaterialData("hello",
|
|
null, 128);
|
|
serverBytes = serverSession.exportKeyingMaterialData("hello",
|
|
null, 128);
|
|
assertEqualsByteArray(clientBytes, serverBytes,
|
|
"Data: No context and exporters are not the same");
|
|
log("Data: No context test passed");
|
|
|
|
// Smaller key size
|
|
clientKey = clientSession.exportKeyingMaterialKey(
|
|
"Generic", "hello", bytes, 5);
|
|
serverKey = serverSession.exportKeyingMaterialKey(
|
|
"Generic", "hello", bytes, 5);
|
|
assertEquals(clientKey, serverKey,
|
|
"Key: Smaller key size should be the same");
|
|
log("Key: Smaller key size test passed");
|
|
|
|
clientBytes = clientSession.exportKeyingMaterialData("hello",
|
|
bytes, 5);
|
|
serverBytes = serverSession.exportKeyingMaterialData("hello",
|
|
bytes, 5);
|
|
assertEqualsByteArray(clientBytes, serverBytes,
|
|
"Data: Smaller key size should be the same");
|
|
log("Data: Smaller key size test passed");
|
|
|
|
// Check error conditions
|
|
|
|
try {
|
|
clientSession.exportKeyingMaterialKey(null, "hello", bytes, 32);
|
|
throw new Exception("null keyAlg accepted");
|
|
} catch (NullPointerException e) {
|
|
log("null keyAlg test passed");
|
|
}
|
|
|
|
try {
|
|
clientSession.exportKeyingMaterialKey("", "hello", bytes, 32);
|
|
throw new Exception("empty keyAlg accepted");
|
|
} catch (IllegalArgumentException e) {
|
|
log("empty keyAlg test passed");
|
|
}
|
|
|
|
try {
|
|
clientSession.exportKeyingMaterialData("hello", bytes, -1);
|
|
throw new Exception("negative length accepted");
|
|
} catch (IllegalArgumentException e) {
|
|
log("negative length test passed");
|
|
}
|
|
|
|
try {
|
|
clientSession.exportKeyingMaterialData("hello", bytes, 0);
|
|
throw new Exception("zero length accepted");
|
|
} catch (IllegalArgumentException e) {
|
|
log("zero length test passed");
|
|
}
|
|
|
|
try {
|
|
clientSession.exportKeyingMaterialData(null, bytes, 128);
|
|
throw new Exception("null label accepted");
|
|
} catch (NullPointerException e) {
|
|
log("null label test passed");
|
|
}
|
|
|
|
try {
|
|
clientSession.exportKeyingMaterialData("", bytes, 128);
|
|
throw new Exception("empty label accepted");
|
|
} catch (IllegalArgumentException e) {
|
|
log("empty label test passed");
|
|
}
|
|
|
|
switch (clientSession.getProtocol()) {
|
|
|
|
case "TLSv1.3":
|
|
// 249 bytes is the max label we can accept (<7..255>, since
|
|
// "tls13 " is added in HkdfLabel)
|
|
String longString =
|
|
"12345678901234567890123456789012345678901234567890" +
|
|
"12345678901234567890123456789012345678901234567890" +
|
|
"12345678901234567890123456789012345678901234567890" +
|
|
"12345678901234567890123456789012345678901234567890" +
|
|
"1234567890123456789012345678901234567890123456789";
|
|
|
|
clientSession.exportKeyingMaterialData(longString, bytes, 128);
|
|
log("large label test passed in TLSv1.3");
|
|
|
|
try {
|
|
clientSession.exportKeyingMaterialData(
|
|
longString + "0", bytes, 128);
|
|
throw new Exception("too large label accepted in TLSv1.3");
|
|
} catch (IllegalArgumentException e) {
|
|
log("too large label test passed in TLSv1.3");
|
|
}
|
|
|
|
// 255 bytes is the max context we can accept (<0..255>)
|
|
clientSession.exportKeyingMaterialData(
|
|
longString, new byte[255], 128);
|
|
log("large context test passed in TLSv1.3");
|
|
|
|
try {
|
|
clientSession.exportKeyingMaterialData(
|
|
longString, new byte[256], 128);
|
|
throw new Exception("too large context accepted in TLSv1.3");
|
|
} catch (IllegalArgumentException e) {
|
|
log("too large context test passed in TLSv1.3");
|
|
}
|
|
|
|
// RFC 5869 says 255*HashLen bytes is the max length we can accept.
|
|
// So we'll choose something a bit bigger than the largest
|
|
// hashLen/ciphertext which is 384 (48 bytes) so this will always
|
|
// fail.
|
|
try {
|
|
clientSession.exportKeyingMaterialData(
|
|
longString, new byte[256], 12240);
|
|
throw new Exception("too large length accepted in TLSv1.3");
|
|
} catch (IllegalArgumentException e) {
|
|
log("too large length test passed in TLSv1.3");
|
|
}
|
|
|
|
// Do a null/byte[0] comparison. Exporters should be the same.
|
|
clientBytes = clientSession.exportKeyingMaterialData("hello",
|
|
null, 128);
|
|
serverBytes = serverSession.exportKeyingMaterialData("hello",
|
|
new byte[0], 128);
|
|
assertEqualsByteArray(clientBytes, serverBytes,
|
|
"Data: null vs. empty context should be the same");
|
|
|
|
break;
|
|
|
|
case "TLSv1":
|
|
case "TLSv1.1":
|
|
case "TLSv1.2":
|
|
// Don't see a limit of the label.length or output length.
|
|
|
|
// Check for large context.length
|
|
try {
|
|
clientSession.exportKeyingMaterialData("hello",
|
|
new byte[1 << 16], 128);
|
|
throw new Exception("large context accepted in " +
|
|
"TLSv1/TLSv1.1/TLSv1.2");
|
|
} catch (IllegalArgumentException e) {
|
|
log("large context passed in TLSv1/TLSv1.1/TLSv1.2");
|
|
}
|
|
|
|
// Do a null/byte[0] comparison. Should NOT be the same.
|
|
clientBytes = clientSession.exportKeyingMaterialData(
|
|
"hello", null, 128);
|
|
serverBytes = serverSession.exportKeyingMaterialData(
|
|
"hello", new byte[0], 128);
|
|
assertNotEqualsByteArray(clientBytes, serverBytes,
|
|
"empty vs. null context but exporters same");
|
|
log("Data: empty vs. null context, " +
|
|
"different exporters test passed");
|
|
|
|
break;
|
|
|
|
default:
|
|
throw new RuntimeException("Unknown protocol: " + clientSession.getProtocol());
|
|
}
|
|
|
|
}
|
|
|
|
static boolean isOpen(SSLEngine engine) {
|
|
return (!engine.isOutboundDone() || !engine.isInboundDone());
|
|
}
|
|
|
|
private static void logEngineStatus(SSLEngine engine) {
|
|
log("\tCurrent HS State: " + engine.getHandshakeStatus());
|
|
log("\tisInboundDone() : " + engine.isInboundDone());
|
|
log("\tisOutboundDone(): " + engine.isOutboundDone());
|
|
}
|
|
|
|
private static void logEngineStatus(
|
|
SSLEngine engine, SSLEngineResult result) {
|
|
log("\tResult Status : " + result.getStatus());
|
|
log("\tResult HS Status : " + result.getHandshakeStatus());
|
|
log("\tEngine HS Status : " + engine.getHandshakeStatus());
|
|
log("\tisInboundDone() : " + engine.isInboundDone());
|
|
log("\tisOutboundDone() : " + engine.isOutboundDone());
|
|
log("\tMore Result : " + result);
|
|
}
|
|
|
|
private static void log(String message) {
|
|
System.err.println(message);
|
|
}
|
|
|
|
// If the result indicates that we have outstanding tasks to do,
|
|
// go ahead and run them in this thread.
|
|
protected static void runDelegatedTasks(SSLEngine engine) throws Exception {
|
|
if (engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
|
|
Runnable runnable;
|
|
while ((runnable = engine.getDelegatedTask()) != null) {
|
|
log(" running delegated task...");
|
|
runnable.run();
|
|
}
|
|
HandshakeStatus hsStatus = engine.getHandshakeStatus();
|
|
if (hsStatus == HandshakeStatus.NEED_TASK) {
|
|
throw new Exception(
|
|
"handshake shouldn't need additional tasks");
|
|
}
|
|
logEngineStatus(engine);
|
|
}
|
|
}
|
|
|
|
// Simple check to make sure everything came across as expected.
|
|
static void checkTransfer(ByteBuffer a, ByteBuffer b)
|
|
throws Exception {
|
|
a.flip();
|
|
b.flip();
|
|
|
|
if (!a.equals(b)) {
|
|
throw new Exception("Data didn't transfer cleanly");
|
|
} else {
|
|
log("\tData transferred cleanly");
|
|
}
|
|
|
|
a.position(a.limit());
|
|
b.position(b.limit());
|
|
a.limit(a.capacity());
|
|
b.limit(b.capacity());
|
|
}
|
|
}
|