Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions google-auth-library-java/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,12 @@
<project.appengine.version>2.0.33</project.appengine.version>
<project.findbugs.version>3.0.2</project.findbugs.version>
<deploy.autorelease>false</deploy.autorelease>
<project.error-prone.version>2.48.0</project.error-prone.version>
<project.error-prone.version>2.50.0</project.error-prone.version>
<project.protobuf.version>4.33.6</project.protobuf.version>
<project.cel.version>0.9.0-proto3</project.cel.version>
<project.tink.version>1.15.0</project.tink.version>
<project.slf4j.version>2.0.17</project.slf4j.version>
<project.gson.version>2.13.2</project.gson.version>
<project.gson.version>2.14.0</project.gson.version>
<project.api-common.version>2.67.0-SNAPSHOT</project.api-common.version><!-- {x-version-update:api-common:current} -->
<project.commons-codec.version>1.18.0</project.commons-codec.version>
<surefire.version>3.5.2</surefire.version>
Expand Down
6 changes: 3 additions & 3 deletions grpc-gcp-java/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@
<site.installationModule>grpc-gcp</site.installationModule>
<api-common.version>2.67.0-SNAPSHOT</api-common.version><!-- {x-version-update:api-common:current} -->
<auto-value.version>1.11.0</auto-value.version>
<error-prone-annotations.version>2.48.0</error-prone-annotations.version>
<error-prone-annotations.version>2.50.0</error-prone-annotations.version>
<google-http-client.version>2.2.0</google-http-client.version>
<gson.version>2.13.2</gson.version>
<gson.version>2.14.0</gson.version>
<guava.version>33.6.0-jre</guava.version>
<protobuf.version>4.33.6</protobuf.version>
<grpc.version>1.82.2</grpc.version>
<grpc.version>1.83.0</grpc.version>
<jsr305.version>3.0.2</jsr305.version>
<junit.version>4.13.2</junit.version>
<opencensus.version>0.31.1</opencensus.version>
Expand Down
2 changes: 1 addition & 1 deletion java-bigtable/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@
<github.global.server>github</github.global.server>
<site.installationModule>google-cloud-bigtable-parent</site.installationModule>
<project.javadoc.protobufBaseURL>https://googleapis.dev/java/google-api-grpc/latest</project.javadoc.protobufBaseURL>
<errorprone.version>2.48.0</errorprone.version>
<errorprone.version>2.50.0</errorprone.version>
<errorprone.javac.args>-g</errorprone.javac.args>
</properties>

Expand Down
2 changes: 1 addition & 1 deletion java-datastore/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@
<github.global.server>github</github.global.server>
<site.installationModule>google-cloud-datastore-parent</site.installationModule>
<project.javadoc.protobufBaseURL>https://googleapis.dev/java/google-api-grpc/latest</project.javadoc.protobufBaseURL>
<errorprone.version>2.48.0</errorprone.version>
<errorprone.version>2.50.0</errorprone.version>
<errorprone.javac.args>-g</errorprone.javac.args>
</properties>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.gax.core.NoCredentialsProvider;
import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider;
import com.google.api.gax.httpjson.HttpJsonConscryptUtils;
import com.google.api.gax.httpjson.HttpJsonMetadata;
import com.google.api.gax.httpjson.InstantiatingHttpJsonChannelProvider;
Expand All @@ -29,6 +30,16 @@
import com.google.showcase.v1beta1.EchoResponse;
import com.google.showcase.v1beta1.EchoSettings;
import com.google.showcase.v1beta1.it.util.HttpJsonCapturingClientInterceptor;
import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.ForwardingClientCall;
import io.grpc.ForwardingClientCallListener;
import io.grpc.Metadata;
import io.grpc.MethodDescriptor;
import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;
import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
Expand Down Expand Up @@ -266,4 +277,104 @@ private static KeyStore loadCaCert(String certPath) throws Exception {
}
return trustStore;
}

/**
* Integration test to verify Post-Quantum Cryptography (PQC) TLS negotiation for gRPC clients.
*
* <p>In gRPC-Java 1.83.0+, the default Netty transport (`grpc-netty-shaded`) bundles BoringSSL
* (`netty-tcnative-boringssl-static`) with built-in PQC hybrid key exchange support (e.g.,
* X25519MLKEM768). No custom socket configurator or security provider swapping is needed.
*
* <p>Because the local Showcase test server uses a self-signed CA certificate (written to {@link
* #DEFAULT_CA_CERT_PATH}), we configure the gRPC transport channel builder directly to trust this
* certificate via {@link GrpcSslContexts#forClient()}. This avoids mutating global JVM system
* properties in {@code setUp()} and ensures HTTP/JSON tests remain completely isolated.
*/
@Test
void testGrpcPqc_withTls() throws Exception {
GrpcTlsCapturingClientInterceptor interceptor = new GrpcTlsCapturingClientInterceptor();

InstantiatingGrpcChannelProvider transportChannelProvider =
EchoSettings.defaultGrpcTransportProviderBuilder()
.setEndpoint(SECURE_ENDPOINT)
.setInterceptorProvider(() -> Collections.singletonList(interceptor))
.setChannelConfigurator(
managedChannelBuilder -> {
if (managedChannelBuilder instanceof NettyChannelBuilder) {
try {
// Explicitly trust the self-signed CA certificate created by the local
// Showcase TLS connection without altering JVM-wide SSL trust stores.
((NettyChannelBuilder) managedChannelBuilder)
.sslContext(
GrpcSslContexts.forClient()
.trustManager(new File(DEFAULT_CA_CERT_PATH))
.build());
} catch (Exception e) {
throw new RuntimeException("Failed to configure gRPC SSL context", e);
}
}
return managedChannelBuilder;
})
.build();

EchoSettings settings =
EchoSettings.newBuilder()
.setCredentialsProvider(NoCredentialsProvider.create())
.setTransportChannelProvider(transportChannelProvider)
.build();

try (EchoClient client = EchoClient.create(settings)) {
EchoResponse response =
client.echo(EchoRequest.newBuilder().setContent("pqc-grpc-tls-test").build());
assertThat(response.getContent()).isEqualTo("pqc-grpc-tls-test");

Metadata capturedHeaders = interceptor.capturedMetadata;
assertThat(capturedHeaders).isNotNull();

// Verify that TLS 1.3 key exchange negotiated the expected PQC hybrid group (X25519MLKEM768).
String negotiatedGroup = getGrpcSingleHeaderString(capturedHeaders, TLS_GROUP_HEADER);
assertThat(negotiatedGroup).isEqualTo(EXPECTED_PQC_GROUP);
}
}

/**
* Private gRPC client interceptor to capture the response headers from the Showcase server to
* verify the PQC algorithm.
*/
private static class GrpcTlsCapturingClientInterceptor implements ClientInterceptor {
final Metadata capturedMetadata = new Metadata();

@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(
next.newCall(method, callOptions)) {
@Override
public void start(Listener<RespT> responseListener, Metadata headers) {
super.start(
new ForwardingClientCallListener.SimpleForwardingClientCallListener<RespT>(
responseListener) {
@Override
public void onHeaders(Metadata headers) {
capturedMetadata.merge(headers);
super.onHeaders(headers);
}
},
headers);
}
};
}
}
Comment on lines +344 to +367

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The capturedMetadata field is mutated on the gRPC transport thread inside onHeaders but read on the main test thread. Since Metadata is not thread-safe and there is no memory barrier or synchronization between these threads, this creates a data race and visibility issue, which can lead to flaky tests.

To ensure thread safety and proper visibility under the Java Memory Model (JMM), declare capturedMetadata as volatile and assign a merged copy of the headers to it inside onHeaders to safely publish the captured metadata.

  private static class GrpcTlsCapturingClientInterceptor implements ClientInterceptor {
    private volatile Metadata capturedMetadata;

    @Override
    public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
        MethodDescriptor<ReqT, RespT> method,
        CallOptions callOptions,
        Channel next) {
      return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(
          next.newCall(method, callOptions)) {
        @Override
        public void start(Listener<RespT> responseListener, Metadata headers) {
          super.start(
              new ForwardingClientCallListener.SimpleForwardingClientCallListener<RespT>(
                  responseListener) {
                @Override
                public void onHeaders(Metadata headers) {
                  Metadata copy = new Metadata();
                  copy.merge(headers);
                  capturedMetadata = copy;
                  super.onHeaders(headers);
                }
              },
              headers);
        }
      };
    }
  }


/**
* Private helper method required to extract a single string header from gRPC {@link Metadata}.
*
* @param metadata the captured gRPC response metadata
* @param name the case-insensitive HTTP/2 header name
* @return the string header value, or {@code null} if not present
*/
private static String getGrpcSingleHeaderString(Metadata metadata, String name) {
Metadata.Key<String> key = Metadata.Key.of(name, Metadata.ASCII_STRING_MARSHALLER);
return metadata.get(key);
}
}
2 changes: 1 addition & 1 deletion librarian.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ tools:
classifier: all-deps
packaging: jar
- name: protoc-gen-java_grpc
version: 1.82.2
version: 1.83.0
group_id: io.grpc
artifact_id: protoc-gen-grpc-java
classifier: linux-x86_64
Expand Down
6 changes: 3 additions & 3 deletions sdk-platform-java/gapic-generator-java-pom-parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@
<!-- External dependencies, especially gRPC and Protobuf version, should be
consistent across modules in this repository -->
<javax.annotation-api.version>1.3.2</javax.annotation-api.version>
<grpc.version>1.82.2</grpc.version>
<grpc.version>1.83.0</grpc.version>
<google.http-client.version>2.2.0</google.http-client.version>
<gson.version>2.13.2</gson.version>
<gson.version>2.14.0</gson.version>
<guava.version>33.6.0-jre</guava.version>
<protobuf.version>4.33.6</protobuf.version>
<opentelemetry.version>1.62.0</opentelemetry.version>
<errorprone.version>2.48.0</errorprone.version>
<errorprone.version>2.50.0</errorprone.version>
<jspecify.version>1.0.0</jspecify.version>
<j2objc-annotations.version>3.1</j2objc-annotations.version>
<threetenbp.version>1.7.0</threetenbp.version>
Expand Down
Loading