blob: 369c8f97542cd9641a2d364a96e63d06fee6c55d [file] [log] [blame]
/*
* Copyright (c) 2018, 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 8164879
* @library /lib/testlibrary ../../
* @summary Verify AES/GCM's limits set in the jdk.tls.keyLimits property
* start a new handshake sequence to renegotiate the symmetric key with an
* SSLSocket connection. This test verifies the handshake method was called
* via debugging info. It does not verify the renegotiation was successful
* as that is very hard.
*
* @run main SSLEngineKeyLimit 0 server AES/GCM/NoPadding keyupdate 1050000
* @run main SSLEngineKeyLimit 1 client AES/GCM/NoPadding keyupdate 2^22
*/
/*
* This test runs in another process so we can monitor the debug
* results. The OutputAnalyzer must see correct debug output to return a
* success.
*/
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.TrustManagerFactory;
import java.io.File;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.Arrays;
import jdk.testlibrary.ProcessTools;
import jdk.testlibrary.Utils;
import jdk.testlibrary.OutputAnalyzer;
public class SSLEngineKeyLimit {
SSLEngine eng;
static ByteBuffer cTos;
static ByteBuffer sToc;
static ByteBuffer outdata;
ByteBuffer buf;
static boolean ready = false;
static String pathToStores = "../../../../javax/net/ssl/etc/";
static String keyStoreFile = "keystore";
static String passwd = "passphrase";
static String keyFilename;
static int dataLen = 10240;
static boolean serverwrite = true;
int totalDataLen = 0;
static boolean sc = true;
int delay = 1;
static boolean readdone = false;
// Turn on debugging
static boolean debug = false;
SSLEngineKeyLimit() {
buf = ByteBuffer.allocate(dataLen*4);
}
/**
* args should have two values: server|client, <limit size>
* Prepending 'p' is for internal use only.
*/
public static void main(String args[]) throws Exception {
for (int i = 0; i < args.length; i++) {
System.out.print(" " + args[i]);
}
System.out.println();
if (args[0].compareTo("p") != 0) {
boolean expectedFail = (Integer.parseInt(args[0]) == 1);
if (expectedFail) {
System.out.println("Test expected to not find updated msg");
}
// Write security property file to overwrite default
File f = new File("keyusage."+ System.nanoTime());
PrintWriter p = new PrintWriter(f);
p.write("jdk.tls.keyLimits=");
for (int i = 2; i < args.length; i++) {
p.write(" "+ args[i]);
}
p.close();
System.setProperty("test.java.opts",
"-Dtest.src=" + System.getProperty("test.src") +
" -Dtest.jdk=" + System.getProperty("test.jdk") +
" -Djavax.net.debug=ssl,handshake" +
" -Djava.security.properties=" + f.getName());
System.out.println("test.java.opts: " +
System.getProperty("test.java.opts"));
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true,
Utils.addTestJavaOpts("SSLEngineKeyLimit", "p", args[1]));
OutputAnalyzer output = ProcessTools.executeProcess(pb);
try {
if (expectedFail) {
output.shouldNotContain("KeyUpdate: write key updated");
output.shouldNotContain("KeyUpdate: read key updated");
} else {
output.shouldContain("trigger key update");
output.shouldContain("KeyUpdate: write key updated");
output.shouldContain("KeyUpdate: read key updated");
}
} catch (Exception e) {
throw e;
} finally {
System.out.println("-- BEGIN Stdout:");
System.out.println(output.getStdout());
System.out.println("-- END Stdout");
System.out.println("-- BEGIN Stderr:");
System.out.println(output.getStderr());
System.out.println("-- END Stderr");
}
return;
}
if (args[0].compareTo("p") != 0) {
throw new Exception ("Tried to run outside of a spawned process");
}
if (args[1].compareTo("client") == 0) {
serverwrite = false;
}
cTos = ByteBuffer.allocateDirect(dataLen*4);
keyFilename =
System.getProperty("test.src", "./") + "/" + pathToStores +
"/" + keyStoreFile;
System.setProperty("javax.net.ssl.keyStore", keyFilename);
System.setProperty("javax.net.ssl.keyStorePassword", passwd);
sToc = ByteBuffer.allocateDirect(dataLen*4);
outdata = ByteBuffer.allocateDirect(dataLen);
byte[] data = new byte[dataLen];
Arrays.fill(data, (byte)0x0A);
outdata.put(data);
outdata.flip();
cTos.clear();
sToc.clear();
Thread ts = new Thread(serverwrite ? new Client() : new Server());
ts.start();
(serverwrite ? new Server() : new Client()).run();
ts.interrupt();
ts.join();
}
private static void doTask(SSLEngineResult result,
SSLEngine engine) throws Exception {
if (result.getHandshakeStatus() ==
SSLEngineResult.HandshakeStatus.NEED_TASK) {
Runnable runnable;
while ((runnable = engine.getDelegatedTask()) != null) {
print("\trunning delegated task...");
runnable.run();
}
SSLEngineResult.HandshakeStatus hsStatus =
engine.getHandshakeStatus();
if (hsStatus == SSLEngineResult.HandshakeStatus.NEED_TASK) {
throw new Exception(
"handshake shouldn't need additional tasks");
}
print("\tnew HandshakeStatus: " + hsStatus);
}
}
static void print(String s) {
if (debug) {
System.out.println(s);
}
}
static void log(String s, SSLEngineResult r) {
if (!debug) {
return;
}
System.out.println(s + ": " +
r.getStatus() + "/" + r.getHandshakeStatus()+ " " +
r.bytesConsumed() + "/" + r.bytesProduced() + " ");
}
void write() throws Exception {
int i = 0;
SSLEngineResult r;
boolean again = true;
while (!ready) {
Thread.sleep(delay);
}
print("Write-side. ");
while (i++ < 150) {
while (sc) {
if (readdone) {
return;
}
Thread.sleep(delay);
}
outdata.rewind();
print("write wrap");
while (true) {
r = eng.wrap(outdata, getWriteBuf());
log("write wrap", r);
if (debug && r.getStatus() != SSLEngineResult.Status.OK) {
print("outdata pos: " + outdata.position() +
" rem: " + outdata.remaining() +
" lim: " + outdata.limit() +
" cap: " + outdata.capacity());
print("writebuf pos: " + getWriteBuf().position() +
" rem: " + getWriteBuf().remaining() +
" lim: " + getWriteBuf().limit() +
" cap: " + getWriteBuf().capacity());
}
if (again && r.getStatus() == SSLEngineResult.Status.OK &&
r.getHandshakeStatus() ==
SSLEngineResult.HandshakeStatus.NEED_WRAP) {
print("again");
again = false;
continue;
}
break;
}
doTask(r, eng);
getWriteBuf().flip();
sc = true;
while (sc) {
if (readdone) {
return;
}
Thread.sleep(delay);
}
while (true) {
buf.clear();
r = eng.unwrap(getReadBuf(), buf);
log("write unwrap", r);
if (debug && r.getStatus() != SSLEngineResult.Status.OK) {
print("buf pos: " + buf.position() +
" rem: " + buf.remaining() +
" lim: " + buf.limit() +
" cap: " + buf.capacity());
print("readbuf pos: " + getReadBuf().position() +
" rem: " + getReadBuf().remaining() +
" lim: " + getReadBuf().limit() +
" cap:" + getReadBuf().capacity());
}
break;
}
doTask(r, eng);
getReadBuf().compact();
print("compacted readbuf pos: " + getReadBuf().position() +
" rem: " + getReadBuf().remaining() +
" lim: " + getReadBuf().limit() +
" cap: " + getReadBuf().capacity());
sc = true;
}
}
void read() throws Exception {
byte b = 0x0B;
ByteBuffer buf2 = ByteBuffer.allocateDirect(dataLen);
SSLEngineResult r = null;
boolean exit, again = true;
while (eng == null) {
Thread.sleep(delay);
}
try {
System.out.println("connected");
print("entering read loop");
ready = true;
while (true) {
while (!sc) {
Thread.sleep(delay);
}
print("read wrap");
exit = false;
while (!exit) {
buf2.put(b);
buf2.flip();
r = eng.wrap(buf2, getWriteBuf());
log("read wrap", r);
if (debug) {
// && r.getStatus() != SSLEngineResult.Status.OK) {
print("buf2 pos: " + buf2.position() +
" rem: " + buf2.remaining() +
" cap: " + buf2.capacity());
print("writebuf pos: " + getWriteBuf().position() +
" rem: " + getWriteBuf().remaining() +
" cap: " + getWriteBuf().capacity());
}
if (again && r.getStatus() == SSLEngineResult.Status.OK &&
r.getHandshakeStatus() ==
SSLEngineResult.HandshakeStatus.NEED_WRAP) {
buf2.compact();
again = false;
continue;
}
exit = true;
}
doTask(r, eng);
buf2.clear();
getWriteBuf().flip();
sc = false;
while (!sc) {
Thread.sleep(delay);
}
while (true) {
buf.clear();
r = eng.unwrap(getReadBuf(), buf);
log("read unwrap", r);
if (debug &&
r.getStatus() != SSLEngineResult.Status.OK) {
print("buf pos " + buf.position() +
" rem: " + buf.remaining() +
" lim: " + buf.limit() +
" cap: " + buf.capacity());
print("readbuf pos: " + getReadBuf().position() +
" rem: " + getReadBuf().remaining() +
" lim: " + getReadBuf().limit() +
" cap: " + getReadBuf().capacity());
doTask(r, eng);
}
if (again && r.getStatus() == SSLEngineResult.Status.OK &&
r.getHandshakeStatus() ==
SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
buf.clear();
print("again");
again = false;
continue;
}
break;
}
buf.clear();
getReadBuf().compact();
totalDataLen += r.bytesProduced();
sc = false;
}
} catch (Exception e) {
sc = false;
readdone = true;
System.out.println(e.getMessage());
e.printStackTrace();
System.out.println("Total data read = " + totalDataLen);
}
}
ByteBuffer getReadBuf() {
return null;
}
ByteBuffer getWriteBuf() {
return null;
}
SSLContext initContext() throws Exception {
SSLContext sc = SSLContext.getInstance("TLSv1.3");
KeyStore ks = KeyStore.getInstance(
new File(System.getProperty("javax.net.ssl.keyStore")),
passwd.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, passwd.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
tmf.init(ks);
sc.init(kmf.getKeyManagers(),
tmf.getTrustManagers(), new SecureRandom());
return sc;
}
static class Server extends SSLEngineKeyLimit implements Runnable {
Server() throws Exception {
super();
eng = initContext().createSSLEngine();
eng.setUseClientMode(false);
eng.setNeedClientAuth(true);
}
public void run() {
try {
if (serverwrite) {
write();
} else {
read();
}
} catch (Exception e) {
System.out.println("server: " + e.getMessage());
e.printStackTrace();
}
System.out.println("Server closed");
}
@Override
ByteBuffer getWriteBuf() {
return sToc;
}
@Override
ByteBuffer getReadBuf() {
return cTos;
}
}
static class Client extends SSLEngineKeyLimit implements Runnable {
Client() throws Exception {
super();
eng = initContext().createSSLEngine();
eng.setUseClientMode(true);
}
public void run() {
try {
if (!serverwrite) {
write();
} else {
read();
}
} catch (Exception e) {
System.out.println("client: " + e.getMessage());
e.printStackTrace();
}
System.out.println("Client closed");
}
@Override
ByteBuffer getWriteBuf() {
return cTos;
}
@Override
ByteBuffer getReadBuf() {
return sToc;
}
}
}