jdk-24/test/jdk/javax/net/ssl/SSLEngine/CheckTlsEngineResults.java
2023-02-03 19:55:54 +00:00

710 lines
26 KiB
Java

/*
* Copyright (c) 2003, 2023, 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.
*/
/*
* @test
* @bug 4948079
* @summary Verify return values from SSLEngine wrap/unwrap (TLSv1.2) operations
*
* @run main CheckTlsEngineResults
*
* @author Brad Wetmore
*/
/*
* This is a simple hack to test a bunch of conditions and check
* their return codes.
*/
import javax.net.ssl.*;
import javax.net.ssl.SSLEngineResult.*;
import java.io.*;
import java.security.*;
import java.nio.*;
public class CheckTlsEngineResults {
private final SSLContext SSL_CONTEXT;
private SSLEngine clientEngine; // client
private SSLEngine serverEngine; // server
private static final String PATH_TO_STORES = "../etc";
private static final String KEYSTORE_FILE = "keystore";
private static final String TRUSTSTORE_FILE = "truststore";
private static final String keyFilename =
System.getProperty("test.src", "./") + "/" + PATH_TO_STORES +
"/" + KEYSTORE_FILE;
private static final String trustFilename =
System.getProperty("test.src", "./") + "/" + PATH_TO_STORES +
"/" + TRUSTSTORE_FILE;
private ByteBuffer clientOut; // write side of clientEngine
private ByteBuffer clientIn; // read side of clientEngine
private ByteBuffer serverOut; // write side of serverEngine
private ByteBuffer serverIn; // read side of serverEngine
private ByteBuffer clientToServer; // "reliable" transport clientEngine->serverEngine
private ByteBuffer serverToClient; // "reliable" transport serverEngine->clientEngine
/*
* Majority of the test case is here, setup is done below.
*/
private void createSSLEngines() throws Exception {
clientEngine = SSL_CONTEXT.createSSLEngine("client", 1);
clientEngine.setUseClientMode(true);
serverEngine = SSL_CONTEXT.createSSLEngine("server", 2);
serverEngine.setUseClientMode(false);
}
private boolean isHandshaking(SSLEngine e) {
return (e.getHandshakeStatus() != HandshakeStatus.NOT_HANDSHAKING);
}
private void checkResult(ByteBuffer bbIn, ByteBuffer bbOut,
SSLEngineResult result,
Status status, HandshakeStatus hsStatus,
int consumed, int produced)
throws Exception {
if ((status != null) && (result.getStatus() != status)) {
throw new Exception("Unexpected Status: need = " + status +
" got = " + result.getStatus());
}
if ((hsStatus != null) && (result.getHandshakeStatus() != hsStatus)) {
throw new Exception("Unexpected hsStatus: need = " + hsStatus +
" got = " + result.getHandshakeStatus());
}
if ((consumed != -1) && (consumed != result.bytesConsumed())) {
throw new Exception("Unexpected consumed: need = " + consumed +
" got = " + result.bytesConsumed());
}
if ((produced != -1) && (produced != result.bytesProduced())) {
throw new Exception("Unexpected produced: need = " + produced +
" got = " + result.bytesProduced());
}
if ((consumed != -1) && (bbIn.position() != result.bytesConsumed())) {
throw new Exception("Consumed " + bbIn.position() +
" != " + consumed);
}
if ((produced != -1) && (bbOut.position() != result.bytesProduced())) {
throw new Exception("produced " + bbOut.position() +
" != " + produced);
}
}
private void test() throws Exception {
createSSLEngines();
createBuffers();
SSLEngineResult result1; // clientEngine's results from last operation
SSLEngineResult result2; // serverEngine's results from last operation
String [] suite1 = new String [] {
"TLS_DHE_RSA_WITH_AES_128_CBC_SHA" };
String [] suite2 = new String [] {
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" };
clientEngine.setEnabledCipherSuites(suite1);
serverEngine.setEnabledCipherSuites(suite1);
log("================");
log("unexpected empty unwrap");
serverToClient.limit(0);
result1 = clientEngine.unwrap(serverToClient, clientIn);
checkResult(serverToClient, clientIn, result1,
Status.OK, HandshakeStatus.NEED_WRAP, 0, 0);
serverToClient.limit(serverToClient.capacity());
log("======================================");
log("Client -> Server [ClientHello]");
result1 = clientEngine.wrap(clientOut, clientToServer);
checkResult(clientOut, clientToServer, result1,
Status.OK, HandshakeStatus.NEED_UNWRAP, 0, -1);
clientToServer.flip();
result2 = serverEngine.unwrap(clientToServer, serverIn);
checkResult(clientToServer, serverIn, result2,
Status.OK, HandshakeStatus.NEED_TASK, result1.bytesProduced(), 0);
runDelegatedTasks(serverEngine);
clientToServer.compact();
log("Check for unwrap when wrap needed");
result2 = serverEngine.unwrap(clientToServer, serverIn);
checkResult(clientToServer, serverIn, result2,
Status.OK, HandshakeStatus.NEED_WRAP, 0, 0);
log("======================================");
log("Server -> Client [ServerHello]");
result2 = serverEngine.wrap(serverOut, serverToClient);
checkResult(serverOut, serverToClient, result2,
Status.OK, HandshakeStatus.NEED_UNWRAP, 0, -1);
serverToClient.flip();
result1 = clientEngine.unwrap(serverToClient, clientIn);
checkResult(serverToClient, clientIn, result1,
Status.OK, HandshakeStatus.NEED_TASK, result2.bytesProduced(), 0);
serverToClient.compact();
runDelegatedTasks(clientEngine);
log("======================================");
log("Client -> Server [ClientKeyExchange]");
result1 = clientEngine.wrap(clientOut, clientToServer);
checkResult(clientOut, clientToServer, result1,
Status.OK, HandshakeStatus.NEED_WRAP, 0, -1);
clientToServer.flip();
result2 = serverEngine.unwrap(clientToServer, serverIn);
checkResult(clientToServer, serverIn, result2,
Status.OK, HandshakeStatus.NEED_TASK, result1.bytesProduced(), 0);
runDelegatedTasks(serverEngine);
clientToServer.compact();
log("======================================");
log("Client -> Server [ChangeCipherSpec]");
result1 = clientEngine.wrap(clientOut, clientToServer);
checkResult(clientOut, clientToServer, result1,
Status.OK, HandshakeStatus.NEED_WRAP, 0, -1);
clientToServer.flip();
result2 = serverEngine.unwrap(clientToServer, serverIn);
checkResult(clientToServer, serverIn, result2,
Status.OK, HandshakeStatus.NEED_UNWRAP,
result1.bytesProduced(), 0);
clientToServer.compact();
log("======================================");
log("Client -> Server [Finished]");
result1 = clientEngine.wrap(clientOut, clientToServer);
checkResult(clientOut, clientToServer, result1,
Status.OK, HandshakeStatus.NEED_UNWRAP, 0, -1);
clientToServer.flip();
result2 = serverEngine.unwrap(clientToServer, serverIn);
checkResult(clientToServer, serverIn, result2,
Status.OK, HandshakeStatus.NEED_WRAP, result1.bytesProduced(), 0);
clientToServer.compact();
log("======================================");
log("Server -> Client [NewSessionTicket]");
result2 = serverEngine.wrap(serverOut, serverToClient);
checkResult(serverOut, serverToClient, result2,
Status.OK, HandshakeStatus.NEED_WRAP, 0, -1);
serverToClient.flip();
result1 = clientEngine.unwrap(serverToClient, clientIn);
checkResult(serverToClient, clientIn, result1,
Status.OK, HandshakeStatus.NEED_UNWRAP, result2.bytesProduced(), 0);
serverToClient.compact();
log("======================================");
log("Server -> Client [ChangeCipherSpec]");
result2 = serverEngine.wrap(serverOut, serverToClient);
checkResult(serverOut, serverToClient, result2,
Status.OK, HandshakeStatus.NEED_WRAP, 0, -1);
serverToClient.flip();
result1 = clientEngine.unwrap(serverToClient, clientIn);
checkResult(serverToClient, clientIn, result1,
Status.OK, HandshakeStatus.NEED_UNWRAP, result2.bytesProduced(), 0);
serverToClient.compact();
log("======================================");
log("Server -> Client [Finished]");
result2 = serverEngine.wrap(serverOut, serverToClient);
checkResult(serverOut, serverToClient, result2,
Status.OK, HandshakeStatus.FINISHED, 0, -1);
serverToClient.flip();
result1 = clientEngine.unwrap(serverToClient, clientIn);
checkResult(serverToClient, clientIn, result1,
Status.OK, HandshakeStatus.FINISHED, result2.bytesProduced(), 0);
serverToClient.compact();
log("======================================");
log("Check Session/Ciphers");
String suite = clientEngine.getSession().getCipherSuite();
if (!suite.equals(suite1[0])) {
throw new Exception("suites not equal: " + suite + "/" +
suite1[0]);
}
suite = serverEngine.getSession().getCipherSuite();
if (!suite.equals(suite1[0])) {
throw new Exception("suites not equal: " + suite + "/" +
suite1[0]);
}
log("======================================");
log("DATA");
result1 = clientEngine.wrap(clientOut, clientToServer);
checkResult(clientOut, clientToServer, result1,
Status.OK, HandshakeStatus.NOT_HANDSHAKING,
clientOut.capacity(), -1);
clientToServer.flip();
result2 = serverEngine.wrap(serverOut, serverToClient);
checkResult(serverOut, serverToClient, result2,
Status.OK, HandshakeStatus.NOT_HANDSHAKING,
serverOut.capacity(), -1);
serverToClient.flip();
SSLEngineResult result3 = clientEngine.unwrap(serverToClient, clientIn);
checkResult(serverToClient, clientIn, result3,
Status.OK, HandshakeStatus.NOT_HANDSHAKING,
result2.bytesProduced(), result2.bytesConsumed());
serverToClient.compact();
SSLEngineResult result4 = serverEngine.unwrap(clientToServer, serverIn);
checkResult(clientToServer, serverIn, result4,
Status.OK, HandshakeStatus.NOT_HANDSHAKING,
result1.bytesProduced(), result1.bytesConsumed());
clientToServer.compact();
clientIn.clear();
serverIn.clear();
clientOut.rewind();
serverOut.rewind();
log("======================================");
log("RENEGOTIATE");
serverEngine.getSession().invalidate();
serverEngine.setNeedClientAuth(true);
clientEngine.setEnabledCipherSuites(suite2);
serverEngine.setEnabledCipherSuites(suite2);
serverEngine.beginHandshake();
log("======================================");
log("Server -> Client [HelloRequest]");
result2 = serverEngine.wrap(serverOut, serverToClient);
checkResult(serverOut, serverToClient, result2,
Status.OK, HandshakeStatus.NEED_UNWRAP, 0, -1);
serverToClient.flip();
result1 = clientEngine.unwrap(serverToClient, clientIn);
checkResult(serverToClient, clientIn, result1,
Status.OK, HandshakeStatus.NEED_TASK, result2.bytesProduced(), 0);
serverToClient.compact();
runDelegatedTasks(clientEngine);
log("======================================");
log("CLient -> Server [ClientHello]");
result1 = clientEngine.wrap(clientOut, clientToServer);
checkResult(clientOut, clientToServer, result1,
Status.OK, HandshakeStatus.NEED_UNWRAP, 0, -1);
clientToServer.flip();
result2 = serverEngine.unwrap(clientToServer, serverIn);
checkResult(clientToServer, serverIn, result2,
Status.OK, HandshakeStatus.NEED_TASK, result1.bytesProduced(), 0);
runDelegatedTasks(serverEngine);
clientToServer.compact();
log("======================================");
log("CLIENT->SERVER DATA IN MIDDLE OF HANDSHAKE");
result1 = clientEngine.wrap(clientOut, clientToServer);
checkResult(clientOut, clientToServer, result1,
Status.OK, HandshakeStatus.NEED_UNWRAP,
clientOut.capacity(), -1);
clientToServer.flip();
result4 = serverEngine.unwrap(clientToServer, serverIn);
checkResult(clientToServer, serverIn, result4,
Status.OK, HandshakeStatus.NEED_WRAP,
result1.bytesProduced(), result1.bytesConsumed());
clientToServer.compact();
serverIn.clear();
clientOut.rewind();
log("======================================");
log("Server -> Client [ServerHello]");
result2 = serverEngine.wrap(serverOut, serverToClient);
checkResult(serverOut, serverToClient, result2,
Status.OK, HandshakeStatus.NEED_UNWRAP, 0, -1);
serverToClient.flip();
result1 = clientEngine.unwrap(serverToClient, clientIn);
checkResult(serverToClient, clientIn, result1,
Status.OK, HandshakeStatus.NEED_TASK, result2.bytesProduced(), 0);
serverToClient.compact();
runDelegatedTasks(clientEngine);
log("======================================");
log("SERVER->CLIENT DATA IN MIDDLE OF HANDSHAKE");
result2 = serverEngine.wrap(serverOut, serverToClient);
checkResult(serverOut, serverToClient, result2,
Status.OK, HandshakeStatus.NEED_UNWRAP,
serverOut.capacity(), -1);
serverToClient.flip();
result3 = clientEngine.unwrap(serverToClient, clientIn);
checkResult(serverToClient, clientIn, result3,
Status.OK, HandshakeStatus.NEED_WRAP,
result2.bytesProduced(), result2.bytesConsumed());
serverToClient.compact();
clientIn.clear();
serverOut.rewind();
log("======================================");
log("Client -> Server [Certificate] and [ClientKeyExchange]");
result1 = clientEngine.wrap(clientOut, clientToServer);
checkResult(clientOut, clientToServer, result1,
Status.OK, HandshakeStatus.NEED_WRAP, 0, -1);
clientToServer.flip();
result2 = serverEngine.unwrap(clientToServer, serverIn);
checkResult(clientToServer, serverIn, result2,
Status.OK, HandshakeStatus.NEED_TASK, result1.bytesProduced(), 0);
runDelegatedTasks(serverEngine);
clientToServer.compact();
log("======================================");
log("Client -> Server [ChangeCipherSpec]");
result1 = clientEngine.wrap(clientOut, clientToServer);
checkResult(clientOut, clientToServer, result1,
Status.OK, HandshakeStatus.NEED_WRAP, 0, -1);
clientToServer.flip();
result2 = serverEngine.unwrap(clientToServer, serverIn);
checkResult(clientToServer, serverIn, result2,
Status.OK, HandshakeStatus.NEED_UNWRAP,
result1.bytesProduced(), 0);
clientToServer.compact();
log("======================================");
log("Client -> Server [Finished]");
result1 = clientEngine.wrap(clientOut, clientToServer);
checkResult(clientOut, clientToServer, result1,
Status.OK, HandshakeStatus.NEED_UNWRAP, 0, -1);
clientToServer.flip();
result2 = serverEngine.unwrap(clientToServer, serverIn);
checkResult(clientToServer, serverIn, result2,
Status.OK, HandshakeStatus.NEED_WRAP, result1.bytesProduced(), 0);
clientToServer.compact();
log("======================================");
log("Server -> Client [NewSessionTicket]");
result2 = serverEngine.wrap(serverOut, serverToClient);
checkResult(serverOut, serverToClient, result2,
Status.OK, HandshakeStatus.NEED_WRAP, 0, -1);
serverToClient.flip();
result1 = clientEngine.unwrap(serverToClient, clientIn);
checkResult(serverToClient, clientIn, result1,
Status.OK, HandshakeStatus.NEED_UNWRAP, result2.bytesProduced(), 0);
serverToClient.compact();
log("======================================");
log("Server -> Client [ChangeCipherSpec]");
result2 = serverEngine.wrap(serverOut, serverToClient);
checkResult(serverOut, serverToClient, result2,
Status.OK, HandshakeStatus.NEED_WRAP, 0, -1);
serverToClient.flip();
result1 = clientEngine.unwrap(serverToClient, clientIn);
checkResult(serverToClient, clientIn, result1,
Status.OK, HandshakeStatus.NEED_UNWRAP, result2.bytesProduced(), 0);
serverToClient.compact();
log("======================================");
log("Server -> Client [Finished]");
result2 = serverEngine.wrap(serverOut, serverToClient);
checkResult(serverOut, serverToClient, result2,
Status.OK, HandshakeStatus.FINISHED, 0, -1);
serverToClient.flip();
result1 = clientEngine.unwrap(serverToClient, clientIn);
checkResult(serverToClient, clientIn, result1,
Status.OK, HandshakeStatus.FINISHED, result2.bytesProduced(), 0);
serverToClient.compact();
log("======================================");
log("Check Session/Ciphers");
suite = clientEngine.getSession().getCipherSuite();
if (!suite.equals(suite2[0])) {
throw new Exception("suites not equal: " + suite + "/" +
suite2[0]);
}
suite = serverEngine.getSession().getCipherSuite();
if (!suite.equals(suite2[0])) {
throw new Exception("suites not equal: " + suite + "/" +
suite2[0]);
}
log("======================================");
log("DATA USING NEW SESSION");
result1 = clientEngine.wrap(clientOut, clientToServer);
checkResult(clientOut, clientToServer, result1,
Status.OK, HandshakeStatus.NOT_HANDSHAKING,
clientOut.capacity(), -1);
clientToServer.flip();
result2 = serverEngine.wrap(serverOut, serverToClient);
checkResult(serverOut, serverToClient, result2,
Status.OK, HandshakeStatus.NOT_HANDSHAKING,
serverOut.capacity(), -1);
serverToClient.flip();
result3 = clientEngine.unwrap(serverToClient, clientIn);
checkResult(serverToClient, clientIn, result3,
Status.OK, HandshakeStatus.NOT_HANDSHAKING,
result2.bytesProduced(), result2.bytesConsumed());
serverToClient.compact();
result4 = serverEngine.unwrap(clientToServer, serverIn);
checkResult(clientToServer, serverIn, result4,
Status.OK, HandshakeStatus.NOT_HANDSHAKING,
result1.bytesProduced(), result1.bytesConsumed());
clientToServer.compact();
clientIn.clear();
serverIn.clear();
clientOut.rewind();
serverOut.rewind();
log("======================================");
log("Server -> Client [CloseNotify]");
if (isHandshaking(clientEngine)) {
throw new Exception("clientEngine IS handshaking");
}
if (isHandshaking(serverEngine)) {
throw new Exception("serverEngine IS handshaking");
}
serverEngine.closeOutbound();
if (!isHandshaking(serverEngine)) {
throw new Exception("serverEngine IS NOT handshaking");
}
clientOut.rewind();
serverOut.rewind();
result2 = serverEngine.wrap(serverOut, serverToClient);
checkResult(serverOut, serverToClient, result2,
Status.CLOSED, HandshakeStatus.NOT_HANDSHAKING, 0, -1);
serverToClient.flip();
if (clientEngine.isInboundDone()) {
throw new Exception("clientEngine inboundDone");
}
result1 = clientEngine.unwrap(serverToClient, clientIn);
checkResult(serverToClient, clientIn, result1,
Status.CLOSED, HandshakeStatus.NEED_WRAP,
result2.bytesProduced(), 0);
serverToClient.compact();
if (!clientEngine.isInboundDone()) {
throw new Exception("clientEngine inboundDone");
}
if (!isHandshaking(clientEngine)) {
throw new Exception("clientEngine IS NOT handshaking");
}
result2 = serverEngine.wrap(serverOut, serverToClient);
checkResult(serverOut, serverToClient, result2,
Status.CLOSED, HandshakeStatus.NOT_HANDSHAKING, 0, 0);
serverToClient.flip();
log("======================================");
log("CloseNotify response");
if (clientEngine.isOutboundDone()) {
throw new Exception("clientEngine outboundDone");
}
result1 = clientEngine.wrap(clientOut, clientToServer);
checkResult(clientOut, clientToServer, result1,
Status.CLOSED, HandshakeStatus.NOT_HANDSHAKING, 0, -1);
if (!clientEngine.isOutboundDone()) {
throw new Exception("clientEngine outboundDone is NOT done");
}
if (isHandshaking(clientEngine)) {
throw new Exception("clientEngine IS handshaking");
}
clientToServer.flip();
if (!serverEngine.isOutboundDone()) {
throw new Exception("clientEngine outboundDone");
}
if (serverEngine.isInboundDone()) {
throw new Exception("clientEngine inboundDone");
}
result2 = serverEngine.unwrap(clientToServer, serverIn);
checkResult(clientToServer, serverIn, result2,
Status.CLOSED, HandshakeStatus.NOT_HANDSHAKING,
result1.bytesProduced(), 0);
if (!serverEngine.isOutboundDone()) {
throw new Exception("clientEngine outboundDone is NOT done");
}
if (!serverEngine.isInboundDone()) {
throw new Exception("clientEngine inboundDone is NOT done");
}
if (isHandshaking(serverEngine)) {
throw new Exception("clientEngine IS handshaking");
}
clientToServer.compact();
}
public static void main(String args[]) throws Exception {
CheckTlsEngineResults cs = new CheckTlsEngineResults();
cs.test();
System.out.println("Test Passed.");
}
/*
* **********************************************************
* Majority of the test case is above, below is just setup stuff
* **********************************************************
*/
public CheckTlsEngineResults() throws Exception {
SSL_CONTEXT = getSSLContext(keyFilename, trustFilename);
}
/*
* Create an initialized SSLContext to use for this test.
*/
private SSLContext getSSLContext(String keyFile, String trustFile)
throws Exception {
KeyStore ks = KeyStore.getInstance("JKS");
KeyStore ts = KeyStore.getInstance("JKS");
char[] passphrase = "passphrase".toCharArray();
ks.load(new FileInputStream(keyFile), passphrase);
ts.load(new FileInputStream(trustFile), passphrase);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, passphrase);
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
SSLContext sslCtx = SSLContext.getInstance("TLSv1.2");
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return sslCtx;
}
private void createBuffers() {
// Size the buffers as appropriate.
SSLSession session = clientEngine.getSession();
int appBufferMax = session.getApplicationBufferSize();
int netBufferMax = session.getPacketBufferSize();
clientIn = ByteBuffer.allocateDirect(appBufferMax + 50);
serverIn = ByteBuffer.allocateDirect(appBufferMax + 50);
clientToServer = ByteBuffer.allocateDirect(netBufferMax);
serverToClient = ByteBuffer.allocateDirect(netBufferMax);
clientOut = ByteBuffer.wrap("Hi Server, I'm Client".getBytes());
serverOut = ByteBuffer.wrap("Hello Client, I'm Server".getBytes());
log("Client out = " + clientOut);
log("Server out = " + serverOut);
log("");
}
private static void runDelegatedTasks(SSLEngine engine) {
Runnable runnable;
while ((runnable = engine.getDelegatedTask()) != null) {
log("Running delegated task...");
runnable.run();
}
}
private static void log(String str) {
System.out.println(str);
}
}