Skip to content

Commit 3152eb4

Browse files
committed
test: trim connectivity test
1 parent 38b7379 commit 3152eb4

1 file changed

Lines changed: 3 additions & 186 deletions

File tree

sdk-platform-java/pqc-test/pqc-test-common/src/main/java/com/google/api/gax/httpjson/PqcConnectivityTest.java

Lines changed: 3 additions & 186 deletions
Original file line numberDiff line numberDiff line change
@@ -34,126 +34,24 @@
3434
import static org.junit.jupiter.api.Assertions.assertNotNull;
3535
import static org.junit.jupiter.api.Assertions.fail;
3636

37-
import com.google.api.gax.core.NoCredentialsProvider;
3837
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
3938
import io.grpc.netty.shaded.io.netty.handler.ssl.ApplicationProtocolConfig;
4039
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext;
4140
import io.grpc.netty.shaded.io.netty.handler.ssl.SslContextBuilder;
42-
import io.grpc.netty.shaded.io.netty.handler.ssl.SslProvider;
4341
import io.grpc.netty.shaded.io.netty.handler.ssl.util.InsecureTrustManagerFactory;
4442
import java.io.InputStream;
4543
import java.security.KeyStore;
46-
import java.util.ArrayList;
47-
import java.util.List;
4844
import org.junit.jupiter.api.AfterAll;
4945
import org.junit.jupiter.api.BeforeAll;
5046
import org.junit.jupiter.api.Test;
5147

52-
/**
53-
* PqcConnectivityTest serves as the base integration validation suite for confirming transparent,
54-
* zero-config Post-Quantum Cryptography (PQC) auto-upgrades across all Google Cloud Java SDK
55-
* transports.
56-
*
57-
* <h3>Design and Architectural Workflow</h3>
58-
*
59-
* <p>The validation framework operates via an end-to-end hermetic handshake architecture:
60-
*
61-
* <pre>
62-
* +---------------------------------------+ +-----------------------------------------+
63-
* | Vanilla App Client Code | | PqcTestServer (Enforces MLKEM768)|
64-
* | (e.g. BigQueryOptions.getDefaultInst) | +-----------------------------------------+
65-
* +---------------------------------------+ ^
66-
* | |
67-
* v |
68-
* +---------------------------------------+ |
69-
* | google-cloud-core-http | |
70-
* | (DefaultHttpTransportFactory) | |
71-
* +---------------------------------------+ |
72-
* | |
73-
* v |
74-
* +---------------------------------------+ |
75-
* | google-http-java-client | |
76-
* | (SslUtils.getTlsSslContext() JJSSE) | |
77-
* +---------------------------------------+ |
78-
* | |
79-
* v |
80-
* +---------------------------------------+ |
81-
* | PqcDelegatingSSLSocketFactory | |
82-
* | (Wraps default BCSSLSocketFactory) | |
83-
* +---------------------------------------+ |
84-
* | |
85-
* +-----------------[TLSv1.3 MLKEM768 Hybrid Handshake]
86-
* </pre>
87-
*
88-
* <ul>
89-
* <li><b>Auto-Upgrade Detection:</b> The test dynamically detects if the current classpath
90-
* includes the snapshot version of <code>google-http-java-client</code> (which contains
91-
* <code>PqcDelegatingSSLSocketFactory</code>).
92-
* <li><b>Zero-Config Integration:</b> If supported, Bouncy Castle JSSE is promoted to the default
93-
* security provider (position 1). The standard client generation libraries automatically wrap
94-
* all outbound transport connections in post-quantum hybrid key exchanges (enforcing
95-
* ML-KEM-768 and classical curves) without requiring manual transport option overrides.
96-
* <li><b>Automatic Fallback:</b> In release test scopes (where older library builds lack PQC
97-
* features), the test silently skips dynamic JCA promotion, validating that classical TLS 1.3
98-
* paths remain fully robust and operational.
99-
* </ul>
100-
*/
10148
public abstract class PqcConnectivityTest {
10249

10350
private static Process serverProcess;
104-
protected static int httpPqcPort;
105-
protected static int httpClassicalPort;
10651
protected static int grpcPqcPort;
10752
protected static int grpcClassicalPort;
108-
private static boolean isPqcSupported;
10953
private static KeyStore ks;
11054

111-
/**
112-
* Configures the integration test harness environment before test cases are executed.
113-
*
114-
* <p><b>Harness Execution Flow:</b>
115-
*
116-
* <ol>
117-
* <li>Extracts the secure PKCS12 validation certificate (<code>pqctest.p12</code>) from the
118-
* classpath to a localized temp file to guarantee isolated execution.
119-
* <li>Configures JVM standard truststore system properties (<code>javax.net.ssl.trustStore
120-
* </code>) to point to the extracted certificate, enabling clean default SSLContext
121-
* verification.
122-
* <li>Inspects the runtime classpath to determine if PQC wrapper auto-upgrades are active.
123-
* <li>If PQC is supported, registers <code>BouncyCastleJsseProvider</code> at position 1. This
124-
* automatically causes all standard vanilla clients instantiating default <code>SSLContext
125-
* </code> to negotiate PQC.
126-
* <li>Spins up the hermetic <code>PqcTestServer</code> in a separate JVM process.
127-
* </ol>
128-
*/
129-
/**
130-
* Configures the integration test harness environment before test cases are executed.
131-
*
132-
* <p><b>Detailed Security & Keystore Configuration Architecture:</b>
133-
*
134-
* <ul>
135-
* <li><b>What is a Keystore (PKCS12):</b> A PKCS12 keystore (<code>pqctest.p12</code>) is a
136-
* secure key database containing the server's private key and its self-signed public
137-
* certificate. The server uses it during the TLS handshake to prove its identity and
138-
* establish a secure channel.
139-
* <li><b>How Encryption Works:</b> The certificate itself does not encrypt message data
140-
* directly. Instead, during the TLS handshake, the client and server negotiate a symmetric
141-
* session key using post-quantum cryptography (ML-KEM). This session key is then used to
142-
* encrypt and decrypt all sent/received HTTP/gRPC data.
143-
* <li><b>Why a Custom Temporary Truststore is Required:</b> Because the server uses a
144-
* self-signed test certificate, it is not signed by any public Certificate Authority (CA)
145-
* trusted by the standard JRE truststore (<code>cacerts</code>). Without registering a
146-
* custom truststore containing this certificate, standard JRE TLS clients will reject the
147-
* connection with an <code>SSLHandshakeException</code>. We extract the certificate to a
148-
* temporary file and point <code>javax.net.ssl.trustStore</code> to it, thereby trusting it
149-
* scope-specifically for this test run without polluting or mutating the user's system-wide
150-
* JRE truststore.
151-
* <li><b>JCA Provider Registration:</b> Registers <code>BouncyCastleJsseProvider</code> at
152-
* provider position 1. This registers Bouncy Castle as the primary security provider,
153-
* causing all standard default <code>SSLContext</code> and vanilla client factories to
154-
* utilize Bouncy Castle JSSE and negotiate PQC automatically.
155-
* </ul>
156-
*/
15755
protected boolean clientSupportsPqc() {
15856
return true;
15957
}
@@ -163,16 +61,6 @@ protected boolean clientSupportsPqc() {
16361
@BeforeAll
16462
public static void setup() throws Exception {
16563

166-
// Dynamically detect if PQC auto-upgrade wrapping is supported by current
167-
// classpath
168-
// dependencies (Snapshot vs Release)
169-
try {
170-
Class.forName("com.google.api.client.http.javanet.PqcPeerHostSSLSocketFactory");
171-
isPqcSupported = true;
172-
} catch (ClassNotFoundException e) {
173-
isPqcSupported = false;
174-
}
175-
17664
ks = KeyStore.getInstance("PKCS12");
17765
try (InputStream is = PqcConnectivityTest.class.getResourceAsStream("/pqctest.p12")) {
17866
if (is == null) {
@@ -201,30 +89,22 @@ public static void setup() throws Exception {
20189
serverProcess.getInputStream(), java.nio.charset.StandardCharsets.UTF_8));
20290

20391
String line;
204-
boolean httpPqcFound = false;
205-
boolean httpClassicalFound = false;
20692
boolean grpcPqcFound = false;
20793
boolean grpcClassicalFound = false;
20894

20995
// Wait for the server process to output its HTTP and gRPC ports
21096
long startTime = System.currentTimeMillis();
21197
while ((line = reader.readLine()) != null) {
21298
System.out.println("[SERVER-OUT] " + line);
213-
if (line.startsWith("HTTP_PQC_PORT: ")) {
214-
httpPqcPort = Integer.parseInt(line.substring(15).trim());
215-
httpPqcFound = true;
216-
} else if (line.startsWith("HTTP_CLASSICAL_PORT: ")) {
217-
httpClassicalPort = Integer.parseInt(line.substring(21).trim());
218-
httpClassicalFound = true;
219-
} else if (line.startsWith("GRPC_PQC_PORT: ")) {
99+
if (line.startsWith("GRPC_PQC_PORT: ")) {
220100
grpcPqcPort = Integer.parseInt(line.substring(15).trim());
221101
grpcPqcFound = true;
222102
} else if (line.startsWith("GRPC_CLASSICAL_PORT: ")) {
223103
grpcClassicalPort = Integer.parseInt(line.substring(21).trim());
224104
grpcClassicalFound = true;
225105
}
226106

227-
if (httpPqcFound && httpClassicalFound && grpcPqcFound && grpcClassicalFound) {
107+
if (grpcPqcFound && grpcClassicalFound) {
228108
break;
229109
}
230110

@@ -236,7 +116,7 @@ public static void setup() throws Exception {
236116
}
237117
}
238118

239-
if (!httpPqcFound || !httpClassicalFound || !grpcPqcFound || !grpcClassicalFound) {
119+
if (!grpcPqcFound || !grpcClassicalFound) {
240120
throw new RuntimeException("PqcTestServer failed to initialize ephemeral ports!");
241121
}
242122

@@ -315,69 +195,6 @@ public void testGrpcClassicalServer() throws Exception {
315195
runGrpcTest(grpcClassicalPort, true);
316196
}
317197

318-
319-
private static void configureGrpcChannelForPqc(io.grpc.ManagedChannelBuilder<?> builder) {
320-
if (!(builder instanceof NettyChannelBuilder)) {
321-
throw new IllegalArgumentException(
322-
"Unsupported channel builder type: " + builder.getClass().getName());
323-
}
324-
NettyChannelBuilder nettyBuilder = (NettyChannelBuilder) builder;
325-
326-
// Ensures HTTP/2 is used as it's required by gRPC
327-
ApplicationProtocolConfig apn =
328-
new ApplicationProtocolConfig(
329-
ApplicationProtocolConfig.Protocol.ALPN,
330-
ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
331-
ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
332-
"h2");
333-
334-
try {
335-
java.security.Provider bcProvider = new org.bouncycastle.jce.provider.BouncyCastleProvider();
336-
java.security.Provider bcJsseProvider =
337-
new org.bouncycastle.jsse.provider.BouncyCastleJsseProvider(bcProvider);
338-
339-
SslContext shadedSslContext =
340-
SslContextBuilder.forClient()
341-
.sslProvider(SslProvider.JDK)
342-
.sslContextProvider(bcJsseProvider)
343-
.protocols("TLSv1.3")
344-
.applicationProtocolConfig(apn)
345-
.trustManager(InsecureTrustManagerFactory.INSTANCE)
346-
.build();
347-
348-
nettyBuilder.sslContext(shadedSslContext);
349-
} catch (Exception e) {
350-
throw new RuntimeException("Failed to configure shaded gRPC Netty channel for PQC", e);
351-
}
352-
}
353-
354-
private static void configureGrpcChannelForClassical(io.grpc.ManagedChannelBuilder<?> builder) {
355-
if (!(builder instanceof NettyChannelBuilder)) {
356-
throw new IllegalArgumentException(
357-
"Unsupported channel builder type: " + builder.getClass().getName());
358-
}
359-
NettyChannelBuilder nettyBuilder = (NettyChannelBuilder) builder;
360-
361-
ApplicationProtocolConfig apn =
362-
new ApplicationProtocolConfig(
363-
ApplicationProtocolConfig.Protocol.ALPN,
364-
ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
365-
ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
366-
"h2");
367-
368-
try {
369-
SslContext shadedSslContext =
370-
SslContextBuilder.forClient()
371-
.applicationProtocolConfig(apn)
372-
.trustManager(InsecureTrustManagerFactory.INSTANCE)
373-
.build();
374-
375-
nettyBuilder.sslContext(shadedSslContext);
376-
} catch (Exception e) {
377-
throw new RuntimeException("Failed to configure shaded gRPC Netty channel for Classical", e);
378-
}
379-
}
380-
381198
private static class ByteMarshaller implements io.grpc.MethodDescriptor.Marshaller<byte[]> {
382199
@Override
383200
public InputStream stream(byte[] value) {

0 commit comments

Comments
 (0)