Path: blob/master/test/jdk/javax/net/ssl/TLSv13/HRRKeyShares.java
41152 views
/*1* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223// SunJSSE does not support dynamic system properties, no way to re-use24// system properties in samevm/agentvm mode. For further debugging output25// set the -Djavax.net.debug=ssl:handshake property on the @run lines.2627/*28* @test29* @bug 824763030* @summary Use two key share entries31* @library /test/lib32* @run main/othervm -Djdk.tls.namedGroups=x25519,secp256r1,secp384r1 HRRKeyShares33*/3435import java.io.ByteArrayOutputStream;36import java.io.DataOutputStream;37import java.io.IOException;38import javax.net.ssl.*;39import javax.net.ssl.SSLEngineResult.*;40import java.nio.ByteBuffer;41import java.util.ArrayList;42import java.util.LinkedHashMap;43import java.util.List;44import java.util.Map;45import java.util.Objects;46import jdk.test.lib.Utils;474849public class HRRKeyShares {5051// Some TLS constants we'll use for testing52private static final int TLS_REC_HANDSHAKE = 22;53private static final int TLS_REC_ALERT = 21;54private static final int HS_MSG_CLIHELLO = 1;55private static final int HS_MSG_SERVHELLO = 2; // Also for HRR56private static final int HELLO_EXT_SUPP_GROUPS = 10;57private static final int HELLO_EXT_SUPP_VERS = 43;58private static final int HELLO_EXT_KEY_SHARE = 51;59private static final int TLS_LEGACY_VER = 0x0303; // TLSv1.260private static final int TLS_PROT_VER_13 = 0x0304; // TLSv1.361private static final int NG_SECP256R1 = 0x0017;62private static final int NG_SECP384R1 = 0x0018;63private static final int NG_X25519 = 0x001D;64private static final int NG_X448 = 0x001E;65private static final int NG_GC512A = 0x0026;66private static final int COMP_NONE = 0;67private static final int ALERT_TYPE_FATAL = 2;68private static final int ALERT_DESC_ILLEGAL_PARAM = 47;69private static final byte[] HRR_RANDOM = Utils.toByteArray(70"CF21AD74E59A6111BE1D8C021E65B891" +71"C2A211167ABB8C5E079E09E2C8A8339C");7273static class ClientHello {74// TLS Record header fields75final int recType;76final int recVers;77final int recLength;7879// Handshake header fields80final int hsMsgType;81final int hsMsgLength;8283// ClientHello fields84final int version;85final byte[] random;86final byte[] sessId;87final List<Integer> cipherSuites = new ArrayList<>();88final List<Integer> compressionList = new ArrayList<>();89final Map<Integer,byte[]> extensionMap = new LinkedHashMap<>();9091// These are fields built from specific extension data fields we92// are interested in for our tests93final List<Integer> suppGroups = new ArrayList<>();94final Map<Integer,byte[]> keyShares = new LinkedHashMap<>();95final List<Integer> suppVersions = new ArrayList<>();9697ClientHello(ByteBuffer data) {98Objects.requireNonNull(data);99data.mark();100101// Process the TLS record header102recType = Byte.toUnsignedInt(data.get());103recVers = Short.toUnsignedInt(data.getShort());104recLength = Short.toUnsignedInt(data.getShort());105if (recType != TLS_REC_HANDSHAKE) {106throw new RuntimeException("Not a Handshake TLS record. " +107"Type = " + recType);108}109110// Process the Handshake message header111int recHdr = data.getInt();112hsMsgType = recHdr >>> 24;113hsMsgLength = recHdr & 0x00FFFFFF;114if (hsMsgType != HS_MSG_CLIHELLO) {115throw new RuntimeException("Not a ClientHello message. " +116"Type = " + hsMsgType);117} else if (hsMsgLength > data.remaining()) {118throw new RuntimeException("Incomplete record in buffer: " +119"Record length = " + hsMsgLength + ", Remaining = " +120data.remaining());121}122123version = Short.toUnsignedInt(data.getShort());124random = new byte[32];125data.get(random);126sessId = new byte[Byte.toUnsignedInt(data.get())];127data.get(sessId);128129int suiteLen = Short.toUnsignedInt(data.getShort());130while (suiteLen > 0) {131cipherSuites.add(Short.toUnsignedInt(data.getShort()));132suiteLen -= 2;133}134135int compLen = Byte.toUnsignedInt(data.get());136while (compLen > 0) {137compressionList.add(Byte.toUnsignedInt(data.get()));138compLen--;139}140141// Extension processing time!142int extListLen = Short.toUnsignedInt(data.getShort());143while (extListLen > 0) {144int extType = Short.toUnsignedInt(data.getShort());145int extLen = Short.toUnsignedInt(data.getShort());146byte[] extData = new byte[extLen];147data.get(extData);148extensionMap.put(extType, extData);149switch (extType) {150case HELLO_EXT_SUPP_GROUPS:151ByteBuffer sgBuf = ByteBuffer.wrap(extData);152int supGrpLen = Short.toUnsignedInt(sgBuf.getShort());153for (int remain = supGrpLen; remain > 0; remain -= 2) {154suppGroups.add(Short.toUnsignedInt(155sgBuf.getShort()));156}157break;158case HELLO_EXT_SUPP_VERS:159ByteBuffer svBuf = ByteBuffer.wrap(extData);160int supVerLen = Byte.toUnsignedInt(svBuf.get());161for (int remain = supVerLen; remain > 0; remain -= 2) {162suppVersions.add(Short.toUnsignedInt(163svBuf.getShort()));164}165break;166case HELLO_EXT_KEY_SHARE:167ByteBuffer ksBuf = ByteBuffer.wrap(extData);168int ksListLen = Short.toUnsignedInt(ksBuf.getShort());169while (ksListLen > 0) {170int namedGroup = Short.toUnsignedInt(171ksBuf.getShort());172int ksLen = Short.toUnsignedInt(ksBuf.getShort());173byte[] ksData = new byte[ksLen];174ksBuf.get(ksData);175keyShares.put(namedGroup, ksData);176ksListLen -= (4 + ksLen);177}178break;179}180extListLen -= (4 + extLen);181}182}183}184185static class Alert {186final int recType;187final int recVers;188final int recLength;189final int alertType;190final int alertDesc;191192Alert(ByteBuffer data) {193Objects.requireNonNull(data);194data.mark();195196// Process the TLS record header197recType = Byte.toUnsignedInt(data.get());198recVers = Short.toUnsignedInt(data.getShort());199recLength = Short.toUnsignedInt(data.getShort());200if (recType != TLS_REC_ALERT) {201throw new RuntimeException("Not a Handshake TLS record. " +202"Type = " + recType);203}204205alertType = Byte.toUnsignedInt(data.get());206alertDesc = Byte.toUnsignedInt(data.get());207}208}209210public static void main(String args[]) throws Exception {211System.out.println("Test 1: Good HRR exchange using secp384r1");212hrrKeyShareTest(NG_SECP384R1, true);213System.out.println();214215System.out.println("Test 2: Bad HRR exchange using secp256r1");216hrrKeyShareTest(NG_SECP256R1, false);217System.out.println();218219System.out.println("Test 3: Bad HRR using unknown GC512A");220hrrKeyShareTest(NG_GC512A, false);221System.out.println();222223System.out.println("Test 4: Bad HRR using known / unasserted x448");224hrrKeyShareTest(NG_X448, false);225System.out.println();226}227228private static void logResult(String str, SSLEngineResult result) {229HandshakeStatus hsStatus = result.getHandshakeStatus();230System.out.println(str +231result.getStatus() + "/" + hsStatus + ", " +232result.bytesConsumed() + "/" + result.bytesProduced() +233" bytes");234if (hsStatus == HandshakeStatus.FINISHED) {235System.out.println("\t...ready for application data");236}237}238239/*240* If the result indicates that we have outstanding tasks to do,241* go ahead and run them in this thread.242*/243private static void runDelegatedTasks(SSLEngine engine) throws Exception {244if (engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {245Runnable runnable;246while ((runnable = engine.getDelegatedTask()) != null) {247System.out.println(" running delegated task...");248runnable.run();249}250HandshakeStatus hsStatus = engine.getHandshakeStatus();251if (hsStatus == HandshakeStatus.NEED_TASK) {252throw new Exception(253"handshake shouldn't need additional tasks");254}255}256}257258/**259* Dump a ByteBuffer as a hexdump to stdout. The dumping routine will260* start at the current position of the buffer and run to its limit.261* After completing the dump, the position will be returned to its262* starting point.263*264* @param data the ByteBuffer to dump to stdout.265*266* @return the hexdump of the byte array.267*/268private static String dumpHexBytes(ByteBuffer data) {269StringBuilder sb = new StringBuilder();270if (data != null) {271int i = 0;272data.mark();273while (data.hasRemaining()) {274if (i % 16 == 0 && i != 0) {275sb.append("\n");276}277sb.append(String.format("%02X ", data.get()));278i++;279}280data.reset();281}282283return sb.toString();284}285286private static void hrrKeyShareTest(int hrrNamedGroup, boolean expectedPass)287throws Exception {288SSLContext sslCtx = SSLContext.getDefault();289SSLEngine engine = sslCtx.createSSLEngine();290engine.setUseClientMode(true);291SSLSession session = engine.getSession();292ByteBuffer clientOut =293ByteBuffer.wrap("I'm a Client".getBytes());294ByteBuffer cTOs = ByteBuffer.allocateDirect(295session.getPacketBufferSize());296297// Create and check the ClientHello message298SSLEngineResult clientResult = engine.wrap(clientOut, cTOs);299logResult("client wrap: ", clientResult);300if (clientResult.getStatus() != SSLEngineResult.Status.OK) {301throw new RuntimeException("Client wrap got status: " +302clientResult.getStatus());303}304305cTOs.flip();306System.out.println("----- ORIGINAL CLIENT HELLO -----\n" +307dumpHexBytes(cTOs));308ClientHello initialCh = new ClientHello(cTOs);309310if (!initialCh.suppVersions.contains(TLS_PROT_VER_13)) {311throw new RuntimeException(312"Missing TLSv1.3 protocol in supported_versions");313} else if (!initialCh.keyShares.containsKey(NG_X25519) ||314!initialCh.keyShares.containsKey(NG_SECP256R1)) {315throw new RuntimeException(316"Missing one or more expected KeyShares");317}318319// Craft the HRR message with the passed-in named group as the320// key share named group to request.321ByteBuffer sTOc = buildHRRMessage(initialCh, hrrNamedGroup);322System.out.println("----- SERVER HELLO RETRY REQUEST -----\n" +323dumpHexBytes(sTOc));324325// Unwrap the HRR and process it326clientResult = engine.unwrap(sTOc, clientOut);327logResult("client unwrap: ", clientResult);328if (clientResult.getStatus() != SSLEngineResult.Status.OK) {329throw new RuntimeException("Client wrap got status: " +330clientResult.getStatus());331}332runDelegatedTasks(engine);333334try {335// Now we're expecting to reissue the ClientHello, this time336// with a secp384r1 share.337cTOs.compact();338clientResult = engine.wrap(clientOut, cTOs);339logResult("client wrap: ", clientResult);340if (clientResult.getStatus() != SSLEngineResult.Status.OK) {341throw new RuntimeException("Client wrap got status: " +342clientResult.getStatus());343}344} catch (RuntimeException | SSLException ssle) {345if (expectedPass) {346System.out.println("Caught unexpected exception");347throw ssle;348} else {349System.out.println("Caught expected exception: " + ssle);350351// Try issuing another wrap call and see if we can get352// the Alert out.353clientResult = engine.wrap(clientOut, cTOs);354logResult("client wrap: ", clientResult);355if (clientResult.getStatus() != SSLEngineResult.Status.CLOSED) {356throw new RuntimeException("Client wrap got status: " +357clientResult.getStatus());358}359360cTOs.flip();361System.out.println("----- ALERT -----\n" + dumpHexBytes(cTOs));362Alert alert = new Alert(cTOs);363if (alert.alertType != ALERT_TYPE_FATAL ||364alert.alertDesc != ALERT_DESC_ILLEGAL_PARAM) {365throw new RuntimeException("Unexpected alert. " +366"received " + alert.alertType + " / " +367alert.alertDesc);368}369return;370}371}372373cTOs.flip();374System.out.println("----- REISSUED CLIENT HELLO -----\n" +375dumpHexBytes(cTOs));376ClientHello reissuedCh = new ClientHello(cTOs);377378if (!reissuedCh.keyShares.containsKey(hrrNamedGroup)) {379throw new RuntimeException("Missing secp384r1 key share");380}381}382383private static ByteBuffer buildHRRMessage(ClientHello cliHello,384int namedGroup) throws IOException {385// Create a ByteBuffer that will be large enough to handle386// the HelloRetryRequest387ByteBuffer hrrBuf = ByteBuffer.allocate(2048); // More than enough!388389// Advance past the TLS record and handshake message headers. We will390// go back later and scribble in the proper lengths. The record header391// is 5 bytes long, the handshake header is 4.392hrrBuf.position(9);393hrrBuf.putShort((short)TLS_LEGACY_VER);394hrrBuf.put(HRR_RANDOM);395hrrBuf.put((byte)cliHello.sessId.length);396hrrBuf.put(cliHello.sessId);397hrrBuf.putShort(cliHello.cipherSuites.get(0).shortValue());398hrrBuf.put((byte)COMP_NONE);399400// Use a separate stream for creating the extension section401ByteArrayOutputStream extBaos = new ByteArrayOutputStream();402DataOutputStream extStream = new DataOutputStream(extBaos);403404// Supported version405extStream.writeShort(HELLO_EXT_SUPP_VERS);406extStream.writeShort(2);407extStream.writeShort(TLS_PROT_VER_13);408409// Key share410extStream.writeShort(HELLO_EXT_KEY_SHARE);411extStream.writeShort(2);412extStream.writeShort(namedGroup);413414// Now add in the extensions into the main message415hrrBuf.putShort((short)extStream.size());416hrrBuf.put(extBaos.toByteArray());417418// At this point we can go back and write in the TLS record and419// handshake message headers.420hrrBuf.flip();421422// Write in the TLS record header423hrrBuf.put((byte)TLS_REC_HANDSHAKE);424hrrBuf.putShort((short)TLS_LEGACY_VER);425hrrBuf.putShort((short)(hrrBuf.limit() - 5));426427// Write the Handshake message header428hrrBuf.putInt((HS_MSG_SERVHELLO << 24) |429((hrrBuf.limit() - 9) & 0x00FFFFFF));430431hrrBuf.rewind();432return hrrBuf;433}434}435436437