/* * Copyright (c) 2021, 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. */ import java.nio.ByteBuffer; import java.util.AbstractMap.SimpleImmutableEntry; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLException; /** * This is not a test. Actual tests are implemented by concrete subclasses. * The abstract class AbstractCheckSignatureSchemes provides a base framework * for checking TLS signature schemes. */ public abstract class AbstractCheckSignatureSchemes extends SSLEngineTemplate { // Helper map to correlate integral SignatureScheme identifiers to // their IANA string name counterparts. protected static final Map sigSchemeMap = Map.ofEntries( new SimpleImmutableEntry(0x0401, "rsa_pkcs1_sha256"), new SimpleImmutableEntry(0x0501, "rsa_pkcs1_sha384"), new SimpleImmutableEntry(0x0601, "rsa_pkcs1_sha512"), new SimpleImmutableEntry(0x0403, "ecdsa_secp256r1_sha256"), new SimpleImmutableEntry(0x0503, "ecdsa_secp384r1_sha384"), new SimpleImmutableEntry(0x0603, "ecdsa_secp521r1_sha512"), new SimpleImmutableEntry(0x0804, "rsa_pss_rsae_sha256"), new SimpleImmutableEntry(0x0805, "rsa_pss_rsae_sha384"), new SimpleImmutableEntry(0x0806, "rsa_pss_rsae_sha512"), new SimpleImmutableEntry(0x0807, "ed25519"), new SimpleImmutableEntry(0x0808, "ed448"), new SimpleImmutableEntry(0x0809, "rsa_pss_pss_sha256"), new SimpleImmutableEntry(0x080a, "rsa_pss_pss_sha384"), new SimpleImmutableEntry(0x080b, "rsa_pss_pss_sha512"), new SimpleImmutableEntry(0x0101, "rsa_md5"), new SimpleImmutableEntry(0x0201, "rsa_pkcs1_sha1"), new SimpleImmutableEntry(0x0202, "dsa_sha1"), new SimpleImmutableEntry(0x0203, "ecdsa_sha1"), new SimpleImmutableEntry(0x0301, "rsa_sha224"), new SimpleImmutableEntry(0x0302, "dsa_sha224"), new SimpleImmutableEntry(0x0303, "ecdsa_sha224"), new SimpleImmutableEntry(0x0402, "rsa_pkcs1_sha256")); // Other useful TLS definitions for these tests protected static final int TLS_HS_CLI_HELLO = 1; protected static final int TLS_HS_CERT_REQ = 13; protected static final int SIG_ALGS_EXT = 13; protected static final int SIG_ALGS_CERT_EXT = 50; protected AbstractCheckSignatureSchemes() throws Exception { super(); } // Returns the protocol for test to use. abstract String getProtocol(); protected boolean isDtls() { return getProtocol().startsWith("DTLS"); } @Override protected SSLEngine configureClientEngine(SSLEngine clientEngine) { clientEngine.setUseClientMode(true); clientEngine.setEnabledProtocols(new String[]{getProtocol()}); return clientEngine; } @Override protected SSLEngine configureServerEngine(SSLEngine serverEngine) { serverEngine.setUseClientMode(false); serverEngine.setWantClientAuth(true); serverEngine.setEnabledProtocols(new String[]{getProtocol()}); return serverEngine; } @Override protected ContextParameters getServerContextParameters() { return new ContextParameters(getProtocol(), "PKIX", "NewSunX509"); } @Override protected ContextParameters getClientContextParameters() { return new ContextParameters(getProtocol(), "PKIX", "NewSunX509"); } protected ByteBuffer extractHandshakeMsg(ByteBuffer tlsRecord, int hsMsgId) throws SSLException { return extractHandshakeMsg(tlsRecord, hsMsgId, isDtls()); } /** * Parses the ClientHello message and extracts from it a list of * SignatureScheme values in string form. It is assumed that the provided * ByteBuffer has its position set at the first byte of the ClientHello * message body (AFTER the handshake header) and contains the entire * hello message. Upon successful completion of this method the ByteBuffer * will have its position reset to the initial offset in the buffer. * If an exception is thrown the position at the time of the exception * will be preserved. * * @param data The ByteBuffer containing the ClientHello bytes. * @param extCode Code of the TLS extension from which to extract * signature schemes. * @return A List of the signature schemes in string form. */ protected List getSigSchemesCliHello( ByteBuffer data, int extCode) { Objects.requireNonNull(data); data.mark(); // Skip over the protocol version and client random data.position(data.position() + 34); // Jump past the session ID (if there is one) int sessLen = Byte.toUnsignedInt(data.get()); if (sessLen != 0) { data.position(data.position() + sessLen); } // Skip DTLS-specific opaque cookie if any if (isDtls()) { int cookieLen = Byte.toUnsignedInt(data.get()); if (cookieLen != 0) { data.position(data.position() + cookieLen); } } // Jump past the cipher suites int csLen = Short.toUnsignedInt(data.getShort()); if (csLen != 0) { data.position(data.position() + csLen); } // ...and the compression int compLen = Byte.toUnsignedInt(data.get()); if (compLen != 0) { data.position(data.position() + compLen); } // Now for the fun part. Go through the extensions and look // for the two status request exts. List extSigAlgs = getSigSchemesFromExt(data, extCode); // We should be at the end of the ClientHello data.reset(); return extSigAlgs; } /** * Parses the CertificateRequest message and extracts from it a list of * SignatureScheme values in string form. It is assumed that the provided * ByteBuffer has its position set at the first byte of the * CertificateRequest message body (AFTER the handshake header) and * contains the entire CR message. Upon successful completion of this * method the ByteBuffer will have its position reset to the initial * offset in the buffer. * If an exception is thrown the position at the time of the exception * will be preserved. * * @param data The ByteBuffer containing the CertificateRequest bytes * * @return A List of the signature schemes in string form. If no * signature_algorithms extension is present in the CertificateRequest * then an empty list will be returned. */ protected List getSigSchemesCertReq(ByteBuffer data) { Objects.requireNonNull(data); data.mark(); // Jump past the certificate types int certTypeLen = Byte.toUnsignedInt(data.get()); if (certTypeLen != 0) { data.position(data.position() + certTypeLen); } // Collect the SignatureAndHashAlgorithms List extSigAlgs = new ArrayList(); int sigSchemeLen = Short.toUnsignedInt(data.getShort()); for (int ssOff = 0; ssOff < sigSchemeLen; ssOff += 2) { String schemeName = sigSchemeMap.get( Short.toUnsignedInt(data.getShort())); if (schemeName != null) { extSigAlgs.add(schemeName); } } data.reset(); return extSigAlgs; } /** * Gets signatures schemes from the given TLS extension. * The buffer should be positioned at the start of the extension. */ protected List getSigSchemesFromExt( ByteBuffer data, int extCode) { List extSigAlgs = new ArrayList<>(); data.getShort(); // read length while (data.hasRemaining()) { int extType = Short.toUnsignedInt(data.getShort()); int extLen = Short.toUnsignedInt(data.getShort()); if (extType == extCode) { // Start processing signature algorithms int sigSchemeLen = Short.toUnsignedInt(data.getShort()); for (int ssOff = 0; ssOff < sigSchemeLen; ssOff += 2) { String schemeName = sigSchemeMap.get( Short.toUnsignedInt(data.getShort())); if (schemeName != null) { extSigAlgs.add(schemeName); } } } else { // Not the extension we're looking for. Skip past the // extension data data.position(data.position() + extLen); } } return extSigAlgs; } }