Merge pull request #376 from cconlon/rsaServerCertChoice
JNI/JSSE: prefer RSA server cert when configured named groups have no ECC curvespull/377/head
commit
a43cdd9d6b
|
|
@ -2189,6 +2189,32 @@ public class WolfSSL {
|
|||
return PQC_GROUP_IDS.contains(Integer.valueOf(namedGroup));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given native named-group enum value identifies a
|
||||
* classic elliptic curve group (SECT/SECP/Brainpool/X25519/X448/SM2),
|
||||
* as opposed to an FFDHE or post-quantum group.
|
||||
*
|
||||
* The classification follows the IANA TLS Supported Groups registry
|
||||
* partition (RFC 7919). Every value in the range 1-255 is treated as
|
||||
* an elliptic curve group, including IANA-assigned curve IDs that do
|
||||
* not yet have WOLFSSL_ECC_* constants on this class (ex:
|
||||
* brainpoolP256r1tls13 through brainpoolP512r1tls13 at 31-33, GOST
|
||||
* curves at 34-40). Values 256-511 are the FFDHE block and PQC
|
||||
* standalone/hybrid groups sit at 512 and above, all returning false.
|
||||
*
|
||||
* @param namedGroup native named-group enum value, typically the
|
||||
* result of {@link #getNamedGroupFromString(String)}
|
||||
* or one of the WOLFSSL_ECC_* constants on this class.
|
||||
* @return true if the group is in the elliptic curve range of the
|
||||
* supported groups registry, false otherwise (including for
|
||||
* {@link #WOLFSSL_NAMED_GROUP_INVALID}, FFDHE groups, and
|
||||
* PQC standalone/hybrid groups).
|
||||
*/
|
||||
public static boolean isECCNamedGroup(int namedGroup) {
|
||||
return (namedGroup > WOLFSSL_NAMED_GROUP_INVALID &&
|
||||
namedGroup < WOLFSSL_FFDHE_2048);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"deprecation", "removal"})
|
||||
@Override
|
||||
protected void finalize() throws Throwable
|
||||
|
|
|
|||
|
|
@ -291,6 +291,20 @@ public class WolfSSLEngineHelper {
|
|||
keyAlgos.add("ML-DSA-87");
|
||||
}
|
||||
|
||||
/* On server side, only prefer an ECC alias when an ECDSA certificate
|
||||
* can actually complete a key exchange for this session. When the
|
||||
* TLS named groups have been explicitly restricted to a list with
|
||||
* no ECC curves (ex: FFDHE groups only via jdk.tls.namedGroups),
|
||||
* ECDHE/ECDH cipher suites cannot be negotiated. */
|
||||
if (!clientMode && keyAlgos.contains("EC") &&
|
||||
!eccCertUsableWithNamedGroups()) {
|
||||
WolfSSLDebug.log(getClass(), WolfSSLDebug.INFO,
|
||||
() -> "no ECC named groups configured for session, " +
|
||||
"preferring non-ECC server cert/key types");
|
||||
keyAlgos.remove("EC");
|
||||
keyAlgos.add("EC");
|
||||
}
|
||||
|
||||
String[] keyTypes = new String[keyAlgos.size()];
|
||||
keyTypes = keyAlgos.toArray(keyTypes);
|
||||
|
||||
|
|
@ -338,6 +352,58 @@ public class WolfSSLEngineHelper {
|
|||
return alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if an ECDSA certificate is usable for key exchange on
|
||||
* this session, given the configured TLS named groups and the
|
||||
* SSLContext protocol version.
|
||||
*
|
||||
* Reads the named-groups configuration with the same source precedence
|
||||
* as setLocalSupportedGroups(): SSLParameters.setNamedGroups(), then
|
||||
* jdk.tls.namedGroups System property, then the
|
||||
* wolfjsse.enabledSupportedCurves Security property.
|
||||
*
|
||||
* Returns true when no source is configured (native default groups
|
||||
* include ECC curves), when the configured list contains at least one
|
||||
* classic ECC curve, or when the SSLContext is (D)TLS 1.3-only, since
|
||||
* TLS 1.3 certificate authentication is independent of the negotiated
|
||||
* key exchange group.
|
||||
*/
|
||||
private boolean eccCertUsableWithNamedGroups() {
|
||||
|
||||
String[] groups =
|
||||
WolfSSLParametersHelper.getNamedGroupsFromParams(this.params);
|
||||
|
||||
if (groups == null) {
|
||||
groups = WolfSSLUtil.getJdkTlsNamedGroups();
|
||||
}
|
||||
|
||||
if (groups == null) {
|
||||
groups = WolfSSLUtil.getSupportedCurves();
|
||||
}
|
||||
|
||||
if (groups == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (String name : groups) {
|
||||
if (WolfSSL.isECCNamedGroup(
|
||||
WolfSSL.getNamedGroupFromString(name))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* No ECC curves configured. ECDHE/ECDH cannot complete on
|
||||
* (D)TLS <= 1.2, but TLS 1.3 does not tie cert type to the group.
|
||||
* Use the context protocol version, not the enabled protocols
|
||||
* list: cert selection can run before the app finishes configuring
|
||||
* the session (ex: SSLEngine.getSession() loads the cert), while
|
||||
* a (D)TLS 1.3-only context can never negotiate lower. */
|
||||
WolfSSL.TLS_VERSION ctxVersion =
|
||||
this.authStore.getProtocolVersion();
|
||||
return (ctxVersion == WolfSSL.TLS_VERSION.TLSv1_3 ||
|
||||
ctxVersion == WolfSSL.TLS_VERSION.DTLSv1_3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the private key and certificate chain for this
|
||||
* SSLSocket/SSLEngine to be used for performing authentication of
|
||||
|
|
|
|||
|
|
@ -22,13 +22,17 @@
|
|||
package com.wolfssl.provider.jsse.test;
|
||||
|
||||
import com.wolfssl.WolfSSL;
|
||||
import com.wolfssl.WolfSSLContext;
|
||||
import com.wolfssl.WolfSSLException;
|
||||
import com.wolfssl.WolfSSLSession;
|
||||
import com.wolfssl.provider.jsse.WolfSSLParametersHelper;
|
||||
import com.wolfssl.provider.jsse.WolfSSLProvider;
|
||||
import com.wolfssl.test.TimedTestWatcher;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.Security;
|
||||
import java.security.cert.Certificate;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
|
|
@ -41,6 +45,7 @@ import static org.junit.Assert.assertArrayEquals;
|
|||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests for TLS named-groups (supported_groups) configuration in wolfJSSE.
|
||||
|
|
@ -348,6 +353,400 @@ public class WolfSSLNamedGroupsTest {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A server whose KeyStore holds both RSA and ECC private keys must
|
||||
* complete a TLS 1.2 handshake when jdk.tls.namedGroups is restricted
|
||||
* to FFDHE groups only.
|
||||
*
|
||||
* Server certificate selection prefers an ECC alias over RSA when the
|
||||
* KeyStore holds both. With no ECC curves in supported_groups, ECDHE
|
||||
* suites cannot be negotiated on TLS 1.2, so an ECDSA certificate
|
||||
* cannot complete any key exchange and the handshake previously failed
|
||||
* with no shared cipher suite. The RSA alias must be preferred instead
|
||||
* so a DHE_RSA suite can complete over the RFC 7919 group.
|
||||
*/
|
||||
@Test
|
||||
public void testJdkTlsNamedGroupsFfdheOnlyTls12PrefersRsaCert()
|
||||
throws Exception {
|
||||
|
||||
Assume.assumeTrue("TLS 1.2, RSA and ECC required in native wolfSSL",
|
||||
WolfSSL.TLSv12Enabled() && WolfSSL.RsaEnabled() &&
|
||||
WolfSSL.EccEnabled());
|
||||
Assume.assumeTrue("DHE_RSA cipher suites not available",
|
||||
dheRsaSuitesAvailable());
|
||||
Assume.assumeTrue("ffdhe2048 named group not supported",
|
||||
ffdhe2048GroupSupported());
|
||||
|
||||
synchronized (WolfSSLPQCTestUtil.GROUP_PROP_LOCK) {
|
||||
Assume.assumeTrue("TLS 1.2 FFDHE handshakes not supported by " +
|
||||
"native wolfSSL build", tls12FfdheHandshakeSupported());
|
||||
|
||||
String prevWolf =
|
||||
WolfSSLPQCTestUtil.setCurvesProperty(null);
|
||||
String prevJdk =
|
||||
WolfSSLPQCTestUtil.setJdkNamedGroupsProperty("ffdhe2048");
|
||||
|
||||
try {
|
||||
SSLContext ctx = tf.createSSLContext("TLSv1.2", PROVIDER);
|
||||
SSLEngine server = ctx.createSSLEngine();
|
||||
server.setUseClientMode(false);
|
||||
server.setNeedClientAuth(false);
|
||||
SSLEngine client =
|
||||
ctx.createSSLEngine("wolfSSL named groups test", 11111);
|
||||
client.setUseClientMode(true);
|
||||
|
||||
int ret = tf.testConnection(server, client,
|
||||
suitesMatching("TLS_DHE_RSA_"),
|
||||
new String[] { "TLSv1.2" }, APP_DATA);
|
||||
assertEquals("TLS 1.2 handshake with FFDHE-only " +
|
||||
"jdk.tls.namedGroups and a KeyStore holding both RSA " +
|
||||
"and ECC keys must succeed", 0, ret);
|
||||
|
||||
String suite = client.getSession().getCipherSuite();
|
||||
assertTrue("negotiated cipher suite must be DHE_RSA, " +
|
||||
"got: " + suite, suite.startsWith("TLS_DHE_RSA_") ||
|
||||
suite.startsWith("DHE-RSA-"));
|
||||
}
|
||||
finally {
|
||||
WolfSSLPQCTestUtil.restoreCurvesProperty(prevWolf);
|
||||
WolfSSLPQCTestUtil.restoreJdkNamedGroupsProperty(prevJdk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* True when native wolfSSL was compiled with DH support, using the
|
||||
* presence of DHE_RSA cipher suites as a proxy. FFDHE named groups
|
||||
* need DH for both TLS 1.2 DHE_RSA suites and TLS 1.3 FFDHE key
|
||||
* shares.
|
||||
*/
|
||||
private static boolean dheRsaSuitesAvailable() {
|
||||
for (String suite : WolfSSL.getCiphersIana()) {
|
||||
if (suite.startsWith("TLS_DHE_RSA_")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return compiled-in cipher suites whose IANA names start with any of
|
||||
* the given prefixes.
|
||||
*/
|
||||
private static String[] suitesMatching(String... prefixes) {
|
||||
|
||||
ArrayList<String> matched = new ArrayList<>();
|
||||
|
||||
for (String suite : WolfSSL.getCiphersIana()) {
|
||||
for (String prefix : prefixes) {
|
||||
if (suite.startsWith(prefix)) {
|
||||
matched.add(suite);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matched.toArray(new String[matched.size()]);
|
||||
}
|
||||
|
||||
/* Cached result of tls12FfdheHandshakeSupported() */
|
||||
private static Boolean tls12FfdheCapable = null;
|
||||
|
||||
/**
|
||||
* True when this environment can complete a TLS 1.2 handshake with
|
||||
* FFDHE-only named groups at all, probed with an RSA-only server
|
||||
* KeyStore so that certificate selection plays no part.
|
||||
*
|
||||
* Caller must hold WolfSSLPQCTestUtil.GROUP_PROP_LOCK.
|
||||
*/
|
||||
private static boolean tls12FfdheHandshakeSupported() throws Exception {
|
||||
|
||||
if (tls12FfdheCapable != null) {
|
||||
return tls12FfdheCapable.booleanValue();
|
||||
}
|
||||
|
||||
String prevJdk =
|
||||
WolfSSLPQCTestUtil.setJdkNamedGroupsProperty("ffdhe2048");
|
||||
|
||||
try {
|
||||
SSLContext srvCtx = tf.createSSLContext("TLSv1.2", PROVIDER,
|
||||
tf.createTrustManager("SunX509", tf.caClientJKS, PROVIDER),
|
||||
tf.createKeyManager("SunX509", tf.serverRSAJKS, PROVIDER));
|
||||
SSLContext cliCtx = tf.createSSLContext("TLSv1.2", PROVIDER,
|
||||
tf.createTrustManager("SunX509", tf.caServerJKS, PROVIDER),
|
||||
tf.createKeyManager("SunX509", tf.clientRSAJKS, PROVIDER));
|
||||
|
||||
SSLEngine server = srvCtx.createSSLEngine();
|
||||
server.setUseClientMode(false);
|
||||
server.setNeedClientAuth(false);
|
||||
SSLEngine client =
|
||||
cliCtx.createSSLEngine("wolfSSL named groups test", 11111);
|
||||
client.setUseClientMode(true);
|
||||
|
||||
int ret = tf.testConnection(server, client,
|
||||
suitesMatching("TLS_DHE_RSA_"),
|
||||
new String[] { "TLSv1.2" }, APP_DATA);
|
||||
tls12FfdheCapable = Boolean.valueOf(ret == 0);
|
||||
}
|
||||
finally {
|
||||
WolfSSLPQCTestUtil.restoreJdkNamedGroupsProperty(prevJdk);
|
||||
}
|
||||
|
||||
return tls12FfdheCapable.booleanValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* True when native wolfSSL supports the ffdhe2048 TLS named group
|
||||
* (HAVE_FFDHE_2048), probed the same way wolfJSSE applies configured
|
||||
* groups to a session.
|
||||
*/
|
||||
private static boolean ffdhe2048GroupSupported() {
|
||||
|
||||
WolfSSLContext ctx = null;
|
||||
WolfSSLSession ssl = null;
|
||||
|
||||
try {
|
||||
ctx = new WolfSSLContext(WolfSSL.SSLv23_ClientMethod());
|
||||
ssl = new WolfSSLSession(ctx);
|
||||
|
||||
return (ssl.useSupportedCurves(
|
||||
new int[] { WolfSSL.WOLFSSL_FFDHE_2048 }) ==
|
||||
WolfSSL.SSL_SUCCESS);
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
} finally {
|
||||
/* Best-effort cleanup, must not throw out of this probe */
|
||||
if (ssl != null) {
|
||||
try {
|
||||
ssl.freeSSL();
|
||||
} catch (Exception e) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
if (ctx != null) {
|
||||
try {
|
||||
ctx.free();
|
||||
} catch (Exception e) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same server RSA-alias preference as
|
||||
* testJdkTlsNamedGroupsFfdheOnlyTls12PrefersRsaCert, driven by the
|
||||
* lowest-precedence configuration source, the
|
||||
* wolfjsse.enabledSupportedCurves Security property.
|
||||
*/
|
||||
@Test
|
||||
public void testWolfjsseCurvesPropertyFfdheOnlyTls12PrefersRsaCert()
|
||||
throws Exception {
|
||||
|
||||
Assume.assumeTrue("TLS 1.2, RSA and ECC required in native wolfSSL",
|
||||
WolfSSL.TLSv12Enabled() && WolfSSL.RsaEnabled() &&
|
||||
WolfSSL.EccEnabled());
|
||||
Assume.assumeTrue("DHE_RSA cipher suites not available",
|
||||
dheRsaSuitesAvailable());
|
||||
Assume.assumeTrue("ffdhe2048 named group not supported",
|
||||
ffdhe2048GroupSupported());
|
||||
|
||||
synchronized (WolfSSLPQCTestUtil.GROUP_PROP_LOCK) {
|
||||
Assume.assumeTrue("TLS 1.2 FFDHE handshakes not supported by " +
|
||||
"native wolfSSL build", tls12FfdheHandshakeSupported());
|
||||
|
||||
String prevJdk =
|
||||
WolfSSLPQCTestUtil.setJdkNamedGroupsProperty(null);
|
||||
String prevWolf =
|
||||
WolfSSLPQCTestUtil.setCurvesProperty("ffdhe2048");
|
||||
|
||||
try {
|
||||
SSLContext ctx = tf.createSSLContext("TLSv1.2", PROVIDER);
|
||||
SSLEngine server = ctx.createSSLEngine();
|
||||
server.setUseClientMode(false);
|
||||
server.setNeedClientAuth(false);
|
||||
SSLEngine client =
|
||||
ctx.createSSLEngine("wolfSSL named groups test", 11111);
|
||||
client.setUseClientMode(true);
|
||||
|
||||
int ret = tf.testConnection(server, client,
|
||||
suitesMatching("TLS_DHE_RSA_"),
|
||||
new String[] { "TLSv1.2" }, APP_DATA);
|
||||
assertEquals("TLS 1.2 handshake with FFDHE-only " +
|
||||
"wolfjsse.enabledSupportedCurves and a KeyStore " +
|
||||
"holding both RSA and ECC keys must succeed", 0, ret);
|
||||
|
||||
String suite = client.getSession().getCipherSuite();
|
||||
assertTrue("negotiated cipher suite must be DHE_RSA, " +
|
||||
"got: " + suite, suite.startsWith("TLS_DHE_RSA_") ||
|
||||
suite.startsWith("DHE-RSA-"));
|
||||
}
|
||||
finally {
|
||||
WolfSSLPQCTestUtil.restoreCurvesProperty(prevWolf);
|
||||
WolfSSLPQCTestUtil.restoreJdkNamedGroupsProperty(prevJdk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same server RSA-alias preference as
|
||||
* testJdkTlsNamedGroupsFfdheOnlyTls12PrefersRsaCert, driven by the
|
||||
* highest-precedence configuration source, per-engine
|
||||
* SSLParameters.setNamedGroups() (JDK 20+).
|
||||
*/
|
||||
@Test
|
||||
public void testSetNamedGroupsFfdheOnlyTls12PrefersRsaCert()
|
||||
throws Exception {
|
||||
|
||||
Assume.assumeTrue("Host JDK lacks SSLParameters.setNamedGroups",
|
||||
jdkHasNamedGroups);
|
||||
Assume.assumeTrue("TLS 1.2, RSA and ECC required in native wolfSSL",
|
||||
WolfSSL.TLSv12Enabled() && WolfSSL.RsaEnabled() &&
|
||||
WolfSSL.EccEnabled());
|
||||
Assume.assumeTrue("DHE_RSA cipher suites not available",
|
||||
dheRsaSuitesAvailable());
|
||||
Assume.assumeTrue("ffdhe2048 named group not supported",
|
||||
ffdhe2048GroupSupported());
|
||||
|
||||
synchronized (WolfSSLPQCTestUtil.GROUP_PROP_LOCK) {
|
||||
Assume.assumeTrue("TLS 1.2 FFDHE handshakes not supported by " +
|
||||
"native wolfSSL build", tls12FfdheHandshakeSupported());
|
||||
|
||||
String prevWolf =
|
||||
WolfSSLPQCTestUtil.setCurvesProperty(null);
|
||||
String prevJdk =
|
||||
WolfSSLPQCTestUtil.setJdkNamedGroupsProperty(null);
|
||||
|
||||
try {
|
||||
SSLContext ctx = tf.createSSLContext("TLSv1.2", PROVIDER);
|
||||
SSLEngine server = ctx.createSSLEngine();
|
||||
server.setUseClientMode(false);
|
||||
server.setNeedClientAuth(false);
|
||||
SSLEngine client =
|
||||
ctx.createSSLEngine("wolfSSL named groups test", 11111);
|
||||
client.setUseClientMode(true);
|
||||
|
||||
setEngineNamedGroups(server, new String[] { "ffdhe2048" });
|
||||
setEngineNamedGroups(client, new String[] { "ffdhe2048" });
|
||||
|
||||
int ret = tf.testConnection(server, client,
|
||||
suitesMatching("TLS_DHE_RSA_"),
|
||||
new String[] { "TLSv1.2" }, APP_DATA);
|
||||
assertEquals("TLS 1.2 handshake with FFDHE-only " +
|
||||
"SSLParameters.setNamedGroups() and a KeyStore " +
|
||||
"holding both RSA and ECC keys must succeed", 0, ret);
|
||||
|
||||
String suite = client.getSession().getCipherSuite();
|
||||
assertTrue("negotiated cipher suite must be DHE_RSA, " +
|
||||
"got: " + suite, suite.startsWith("TLS_DHE_RSA_") ||
|
||||
suite.startsWith("DHE-RSA-"));
|
||||
}
|
||||
finally {
|
||||
WolfSSLPQCTestUtil.restoreCurvesProperty(prevWolf);
|
||||
WolfSSLPQCTestUtil.restoreJdkNamedGroupsProperty(prevJdk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* On a TLS 1.3-only session, FFDHE-only named groups must not change
|
||||
* server certificate selection: TLS 1.3 negotiates the key exchange
|
||||
* group independently of the certificate type, so the default ECC
|
||||
* alias preference stays and the handshake completes with the ECDSA
|
||||
* certificate over an ffdhe2048 key share.
|
||||
*/
|
||||
@Test
|
||||
public void testJdkTlsNamedGroupsFfdheOnlyTls13KeepsEccCert()
|
||||
throws Exception {
|
||||
|
||||
Assume.assumeTrue("TLS 1.3, RSA and ECC required in native wolfSSL",
|
||||
WolfSSL.TLSv13Enabled() && WolfSSL.RsaEnabled() &&
|
||||
WolfSSL.EccEnabled());
|
||||
Assume.assumeTrue("ffdhe2048 named group not supported",
|
||||
ffdhe2048GroupSupported());
|
||||
|
||||
synchronized (WolfSSLPQCTestUtil.GROUP_PROP_LOCK) {
|
||||
String prevWolf =
|
||||
WolfSSLPQCTestUtil.setCurvesProperty(null);
|
||||
String prevJdk =
|
||||
WolfSSLPQCTestUtil.setJdkNamedGroupsProperty("ffdhe2048");
|
||||
|
||||
try {
|
||||
SSLContext ctx = tf.createSSLContext("TLSv1.3", PROVIDER);
|
||||
SSLEngine server = ctx.createSSLEngine();
|
||||
server.setUseClientMode(false);
|
||||
server.setNeedClientAuth(false);
|
||||
SSLEngine client =
|
||||
ctx.createSSLEngine("wolfSSL named groups test", 11111);
|
||||
client.setUseClientMode(true);
|
||||
|
||||
int ret = tf.testConnection(server, client, null,
|
||||
new String[] { "TLSv1.3" }, APP_DATA);
|
||||
assertEquals("TLS 1.3 handshake with FFDHE-only " +
|
||||
"jdk.tls.namedGroups must succeed", 0, ret);
|
||||
|
||||
Certificate[] peerCerts =
|
||||
client.getSession().getPeerCertificates();
|
||||
String keyAlgo = peerCerts[0].getPublicKey().getAlgorithm();
|
||||
assertEquals("TLS 1.3 server must keep default ECC cert " +
|
||||
"preference with FFDHE-only named groups", "EC",
|
||||
keyAlgo);
|
||||
}
|
||||
finally {
|
||||
WolfSSLPQCTestUtil.restoreCurvesProperty(prevWolf);
|
||||
WolfSSLPQCTestUtil.restoreJdkNamedGroupsProperty(prevJdk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When the configured named groups contain at least one ECC curve
|
||||
* alongside FFDHE groups, server certificate selection must keep the
|
||||
* default ECC preference and negotiate an ECDHE_ECDSA suite on
|
||||
* TLS 1.2.
|
||||
*/
|
||||
@Test
|
||||
public void testJdkTlsNamedGroupsMixedEccFfdheTls12KeepsEccCert()
|
||||
throws Exception {
|
||||
|
||||
Assume.assumeTrue("TLS 1.2, RSA and ECC required in native wolfSSL",
|
||||
WolfSSL.TLSv12Enabled() && WolfSSL.RsaEnabled() &&
|
||||
WolfSSL.EccEnabled());
|
||||
|
||||
synchronized (WolfSSLPQCTestUtil.GROUP_PROP_LOCK) {
|
||||
String prevWolf =
|
||||
WolfSSLPQCTestUtil.setCurvesProperty(null);
|
||||
String prevJdk = WolfSSLPQCTestUtil.setJdkNamedGroupsProperty(
|
||||
"secp256r1,ffdhe2048");
|
||||
|
||||
try {
|
||||
SSLContext ctx = tf.createSSLContext("TLSv1.2", PROVIDER);
|
||||
SSLEngine server = ctx.createSSLEngine();
|
||||
server.setUseClientMode(false);
|
||||
server.setNeedClientAuth(false);
|
||||
SSLEngine client =
|
||||
ctx.createSSLEngine("wolfSSL named groups test", 11111);
|
||||
client.setUseClientMode(true);
|
||||
|
||||
int ret = tf.testConnection(server, client,
|
||||
suitesMatching("TLS_ECDHE_ECDSA_", "TLS_DHE_RSA_"),
|
||||
new String[] { "TLSv1.2" }, APP_DATA);
|
||||
assertEquals("TLS 1.2 handshake with mixed ECC+FFDHE " +
|
||||
"jdk.tls.namedGroups must succeed", 0, ret);
|
||||
|
||||
String suite = client.getSession().getCipherSuite();
|
||||
assertTrue("negotiated cipher suite must be ECDHE_ECDSA " +
|
||||
"when an ECC group is configured, got: " + suite,
|
||||
suite.startsWith("TLS_ECDHE_ECDSA_") ||
|
||||
suite.startsWith("ECDHE-ECDSA-"));
|
||||
}
|
||||
finally {
|
||||
WolfSSLPQCTestUtil.restoreCurvesProperty(prevWolf);
|
||||
WolfSSLPQCTestUtil.restoreJdkNamedGroupsProperty(prevJdk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When both static properties are set, jdk.tls.namedGroups overrides
|
||||
* wolfjsse.enabledSupportedCurves. A single source is applied per
|
||||
|
|
|
|||
|
|
@ -517,6 +517,51 @@ public class WolfSSLTest {
|
|||
assertFalse(WolfSSL.isPQCNamedGroup(-1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_isECCNamedGroup() {
|
||||
|
||||
/* Classic ECC curves return true, spanning the low and high ends
|
||||
* of the curve ID range (SECT, SECP, Brainpool, X25519/X448, SM2). */
|
||||
assertTrue(WolfSSL.isECCNamedGroup(WolfSSL.WOLFSSL_ECC_SECT163K1));
|
||||
assertTrue(WolfSSL.isECCNamedGroup(WolfSSL.WOLFSSL_ECC_SECP256R1));
|
||||
assertTrue(WolfSSL.isECCNamedGroup(WolfSSL.WOLFSSL_ECC_SECP384R1));
|
||||
assertTrue(WolfSSL.isECCNamedGroup(WolfSSL.WOLFSSL_ECC_SECP521R1));
|
||||
assertTrue(WolfSSL.isECCNamedGroup(
|
||||
WolfSSL.WOLFSSL_ECC_BRAINPOOLP512R1));
|
||||
assertTrue(WolfSSL.isECCNamedGroup(WolfSSL.WOLFSSL_ECC_X25519));
|
||||
assertTrue(WolfSSL.isECCNamedGroup(WolfSSL.WOLFSSL_ECC_X448));
|
||||
assertTrue(WolfSSL.isECCNamedGroup(WolfSSL.WOLFSSL_ECC_SM2P256V1));
|
||||
|
||||
/* The whole registry range below the FFDHE block classifies as
|
||||
* ECC (RFC 7919 partition), including IANA-assigned curve IDs
|
||||
* without WOLFSSL_ECC_* constants yet: 31 is
|
||||
* brainpoolP256r1tls13 (RFC 8734), 34 is GC256A (RFC 9189), and
|
||||
* 255 is the top of the elliptic curve range. */
|
||||
assertTrue(WolfSSL.isECCNamedGroup(31));
|
||||
assertTrue(WolfSSL.isECCNamedGroup(34));
|
||||
assertTrue(WolfSSL.isECCNamedGroup(255));
|
||||
|
||||
/* FFDHE groups and the reserved FFDHE block (256-511) return
|
||||
* false. */
|
||||
assertFalse(WolfSSL.isECCNamedGroup(WolfSSL.WOLFSSL_FFDHE_2048));
|
||||
assertFalse(WolfSSL.isECCNamedGroup(WolfSSL.WOLFSSL_FFDHE_8192));
|
||||
assertFalse(WolfSSL.isECCNamedGroup(511));
|
||||
|
||||
/* PQC standalone and hybrid groups return false, including
|
||||
* hybrids with an ECDHE component. */
|
||||
assertFalse(WolfSSL.isECCNamedGroup(WolfSSL.WOLFSSL_ML_KEM_768));
|
||||
assertFalse(WolfSSL.isECCNamedGroup(
|
||||
WolfSSL.WOLFSSL_X25519MLKEM768));
|
||||
assertFalse(WolfSSL.isECCNamedGroup(
|
||||
WolfSSL.WOLFSSL_SECP256R1MLKEM768));
|
||||
|
||||
/* Sentinel and out-of-range integers return false. */
|
||||
assertFalse(WolfSSL.isECCNamedGroup(
|
||||
WolfSSL.WOLFSSL_NAMED_GROUP_INVALID));
|
||||
assertFalse(WolfSSL.isECCNamedGroup(0));
|
||||
assertFalse(WolfSSL.isECCNamedGroup(-1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_PQC_FeatureDetect_NativeReturns() {
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue