s2a: Use protos published under com.google.s2a.proto.v2. (#11908)

This commit is contained in:
Riya Mehta 2025-02-19 16:59:50 -08:00 committed by GitHub
parent 60f6ea7b8e
commit 68d79b5130
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 78 additions and 919 deletions

View File

@ -19,6 +19,7 @@ IO_GRPC_GRPC_JAVA_ARTIFACTS = [
"com.google.guava:failureaccess:1.0.1",
"com.google.guava:guava:33.3.1-android",
"com.google.re2j:re2j:1.8",
"com.google.s2a.proto.v2:s2a-proto:0.1.1",
"com.google.truth:truth:1.4.2",
"com.squareup.okhttp:okhttp:2.7.5",
"com.squareup.okio:okio:2.10.0", # 3.0+ needs swapping to -jvm; need work to avoid flag-day

View File

@ -8,7 +8,7 @@ curl -Ls https://github.com/grpc/grpc-proto/archive/master.tar.gz | tar xz -C "$
base="$tmpdir/grpc-proto-master"
# Copy protos in 'src/main/proto' from grpc-proto for these projects
for project in alts grpclb services s2a rls interop-testing; do
for project in alts grpclb services rls interop-testing; do
while read -r proto; do
[ -f "$base/$proto" ] && cp "$base/$proto" "$project/src/main/proto/$proto"
echo "$proto"

View File

@ -103,6 +103,7 @@ protobuf-javalite = { module = "com.google.protobuf:protobuf-javalite", version.
protobuf-protoc = { module = "com.google.protobuf:protoc", version.ref = "protobuf" }
re2j = "com.google.re2j:re2j:1.8"
robolectric = "org.robolectric:robolectric:4.14.1"
s2a-proto = "com.google.s2a.proto.v2:s2a-proto:0.1.1"
signature-android = "net.sf.androidscents.signature:android-api-level-21:5.0.1_r2"
signature-java = "org.codehaus.mojo.signature:java18:1.0"
# 11.0.0+ require Java 17+

View File

@ -23,6 +23,7 @@ IO_GRPC_GRPC_JAVA_ARTIFACTS = [
"com.google.guava:failureaccess:1.0.1",
"com.google.guava:guava:33.3.1-android",
"com.google.re2j:re2j:1.8",
"com.google.s2a.proto.v2:s2a-proto:0.1.1",
"com.google.truth:truth:1.4.2",
"com.squareup.okhttp:okhttp:2.7.5",
"com.squareup.okio:okio:2.10.0", # 3.0+ needs swapping to -jvm; need work to avoid flag-day

View File

@ -1,5 +1,3 @@
load("@rules_proto//proto:defs.bzl", "proto_library")
load("//:java_grpc_library.bzl", "java_grpc_library")
load("@rules_jvm_external//:defs.bzl", "artifact")
java_library(
@ -25,9 +23,9 @@ java_library(
name = "s2a_identity",
srcs = ["src/main/java/io/grpc/s2a/internal/handshaker/S2AIdentity.java"],
deps = [
":common_java_proto",
artifact("com.google.errorprone:error_prone_annotations"),
artifact("com.google.guava:guava"),
artifact("com.google.s2a.proto.v2:s2a-proto"),
],
)
@ -58,11 +56,8 @@ java_library(
],
deps = [
":token_manager",
":common_java_proto",
":s2a_channel_pool",
":s2a_identity",
":s2a_java_proto",
":s2a_java_grpc_proto",
"//api",
"//core:internal",
"//netty",
@ -70,6 +65,7 @@ java_library(
artifact("com.google.code.findbugs:jsr305"),
artifact("com.google.errorprone:error_prone_annotations"),
artifact("com.google.guava:guava"),
artifact("com.google.s2a.proto.v2:s2a-proto"),
artifact("org.checkerframework:checker-qual"),
"@com_google_protobuf//:protobuf_java",
artifact("io.netty:netty-common"),
@ -94,67 +90,4 @@ java_library(
artifact("com.google.guava:guava"),
artifact("org.checkerframework:checker-qual"),
],
)
# bazel only accepts proto import with absolute path.
genrule(
name = "protobuf_imports",
srcs = glob(["src/main/proto/grpc/gcp/s2a/*.proto"]),
outs = [
"protobuf_out/grpc/gcp/s2a/s2a.proto",
"protobuf_out/grpc/gcp/s2a/s2a_context.proto",
"protobuf_out/grpc/gcp/s2a/common.proto",
],
cmd = "for fname in $(SRCS); do " +
"sed 's,import \",import \"s2a/protobuf_out/,g' $$fname > " +
"$(@D)/protobuf_out/grpc/gcp/s2a/$$(basename $$fname); done",
)
proto_library(
name = "common_proto",
srcs = [
"protobuf_out/grpc/gcp/s2a/common.proto",
],
)
proto_library(
name = "s2a_context_proto",
srcs = [
"protobuf_out/grpc/gcp/s2a/s2a_context.proto",
],
deps = [
":common_proto",
],
)
proto_library(
name = "s2a_proto",
srcs = [
"protobuf_out/grpc/gcp/s2a/s2a.proto",
],
deps = [
":common_proto",
":s2a_context_proto",
],
)
java_proto_library(
name = "s2a_java_proto",
deps = [":s2a_proto"],
)
java_proto_library(
name = "s2a_context_java_proto",
deps = [":s2a_context_proto"],
)
java_proto_library(
name = "common_java_proto",
deps = [":common_proto"],
)
java_grpc_library(
name = "s2a_java_grpc_proto",
srcs = [":s2a_proto"],
deps = [":s2a_java_proto"],
)
)

View File

@ -11,6 +11,7 @@ plugins {
description = "gRPC: S2A"
dependencies {
implementation libraries.s2a.proto
api project(':grpc-api')
implementation project(':grpc-stub'),

View File

@ -1,330 +0,0 @@
package io.grpc.s2a.internal.handshaker;
import static io.grpc.MethodDescriptor.generateFullMethodName;
/**
*/
@javax.annotation.Generated(
value = "by gRPC proto compiler",
comments = "Source: grpc/gcp/s2a/s2a.proto")
@io.grpc.stub.annotations.GrpcGenerated
public final class S2AServiceGrpc {
private S2AServiceGrpc() {}
public static final java.lang.String SERVICE_NAME = "grpc.gcp.s2a.S2AService";
// Static method descriptors that strictly reflect the proto.
private static volatile io.grpc.MethodDescriptor<io.grpc.s2a.internal.handshaker.SessionReq,
io.grpc.s2a.internal.handshaker.SessionResp> getSetUpSessionMethod;
@io.grpc.stub.annotations.RpcMethod(
fullMethodName = SERVICE_NAME + '/' + "SetUpSession",
requestType = io.grpc.s2a.internal.handshaker.SessionReq.class,
responseType = io.grpc.s2a.internal.handshaker.SessionResp.class,
methodType = io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING)
public static io.grpc.MethodDescriptor<io.grpc.s2a.internal.handshaker.SessionReq,
io.grpc.s2a.internal.handshaker.SessionResp> getSetUpSessionMethod() {
io.grpc.MethodDescriptor<io.grpc.s2a.internal.handshaker.SessionReq, io.grpc.s2a.internal.handshaker.SessionResp> getSetUpSessionMethod;
if ((getSetUpSessionMethod = S2AServiceGrpc.getSetUpSessionMethod) == null) {
synchronized (S2AServiceGrpc.class) {
if ((getSetUpSessionMethod = S2AServiceGrpc.getSetUpSessionMethod) == null) {
S2AServiceGrpc.getSetUpSessionMethod = getSetUpSessionMethod =
io.grpc.MethodDescriptor.<io.grpc.s2a.internal.handshaker.SessionReq, io.grpc.s2a.internal.handshaker.SessionResp>newBuilder()
.setType(io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING)
.setFullMethodName(generateFullMethodName(SERVICE_NAME, "SetUpSession"))
.setSampledToLocalTracing(true)
.setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
io.grpc.s2a.internal.handshaker.SessionReq.getDefaultInstance()))
.setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller(
io.grpc.s2a.internal.handshaker.SessionResp.getDefaultInstance()))
.setSchemaDescriptor(new S2AServiceMethodDescriptorSupplier("SetUpSession"))
.build();
}
}
}
return getSetUpSessionMethod;
}
/**
* Creates a new async stub that supports all call types for the service
*/
public static S2AServiceStub newStub(io.grpc.Channel channel) {
io.grpc.stub.AbstractStub.StubFactory<S2AServiceStub> factory =
new io.grpc.stub.AbstractStub.StubFactory<S2AServiceStub>() {
@java.lang.Override
public S2AServiceStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new S2AServiceStub(channel, callOptions);
}
};
return S2AServiceStub.newStub(factory, channel);
}
/**
* Creates a new blocking-style stub that supports all types of calls on the service
*/
public static S2AServiceBlockingV2Stub newBlockingV2Stub(
io.grpc.Channel channel) {
io.grpc.stub.AbstractStub.StubFactory<S2AServiceBlockingV2Stub> factory =
new io.grpc.stub.AbstractStub.StubFactory<S2AServiceBlockingV2Stub>() {
@java.lang.Override
public S2AServiceBlockingV2Stub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new S2AServiceBlockingV2Stub(channel, callOptions);
}
};
return S2AServiceBlockingV2Stub.newStub(factory, channel);
}
/**
* Creates a new blocking-style stub that supports unary and streaming output calls on the service
*/
public static S2AServiceBlockingStub newBlockingStub(
io.grpc.Channel channel) {
io.grpc.stub.AbstractStub.StubFactory<S2AServiceBlockingStub> factory =
new io.grpc.stub.AbstractStub.StubFactory<S2AServiceBlockingStub>() {
@java.lang.Override
public S2AServiceBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new S2AServiceBlockingStub(channel, callOptions);
}
};
return S2AServiceBlockingStub.newStub(factory, channel);
}
/**
* Creates a new ListenableFuture-style stub that supports unary calls on the service
*/
public static S2AServiceFutureStub newFutureStub(
io.grpc.Channel channel) {
io.grpc.stub.AbstractStub.StubFactory<S2AServiceFutureStub> factory =
new io.grpc.stub.AbstractStub.StubFactory<S2AServiceFutureStub>() {
@java.lang.Override
public S2AServiceFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new S2AServiceFutureStub(channel, callOptions);
}
};
return S2AServiceFutureStub.newStub(factory, channel);
}
/**
*/
public interface AsyncService {
/**
* <pre>
* SetUpSession is a bidirectional stream used by applications to offload
* operations from the TLS handshake.
* </pre>
*/
default io.grpc.stub.StreamObserver<io.grpc.s2a.internal.handshaker.SessionReq> setUpSession(
io.grpc.stub.StreamObserver<io.grpc.s2a.internal.handshaker.SessionResp> responseObserver) {
return io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall(getSetUpSessionMethod(), responseObserver);
}
}
/**
* Base class for the server implementation of the service S2AService.
*/
public static abstract class S2AServiceImplBase
implements io.grpc.BindableService, AsyncService {
@java.lang.Override public final io.grpc.ServerServiceDefinition bindService() {
return S2AServiceGrpc.bindService(this);
}
}
/**
* A stub to allow clients to do asynchronous rpc calls to service S2AService.
*/
public static final class S2AServiceStub
extends io.grpc.stub.AbstractAsyncStub<S2AServiceStub> {
private S2AServiceStub(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
super(channel, callOptions);
}
@java.lang.Override
protected S2AServiceStub build(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new S2AServiceStub(channel, callOptions);
}
/**
* <pre>
* SetUpSession is a bidirectional stream used by applications to offload
* operations from the TLS handshake.
* </pre>
*/
public io.grpc.stub.StreamObserver<io.grpc.s2a.internal.handshaker.SessionReq> setUpSession(
io.grpc.stub.StreamObserver<io.grpc.s2a.internal.handshaker.SessionResp> responseObserver) {
return io.grpc.stub.ClientCalls.asyncBidiStreamingCall(
getChannel().newCall(getSetUpSessionMethod(), getCallOptions()), responseObserver);
}
}
/**
* A stub to allow clients to do synchronous rpc calls to service S2AService.
*/
public static final class S2AServiceBlockingV2Stub
extends io.grpc.stub.AbstractBlockingStub<S2AServiceBlockingV2Stub> {
private S2AServiceBlockingV2Stub(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
super(channel, callOptions);
}
@java.lang.Override
protected S2AServiceBlockingV2Stub build(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new S2AServiceBlockingV2Stub(channel, callOptions);
}
/**
* <pre>
* SetUpSession is a bidirectional stream used by applications to offload
* operations from the TLS handshake.
* </pre>
*/
@io.grpc.ExperimentalApi("https://github.com/grpc/grpc-java/issues/10918")
public io.grpc.stub.BlockingClientCall<io.grpc.s2a.internal.handshaker.SessionReq, io.grpc.s2a.internal.handshaker.SessionResp>
setUpSession() {
return io.grpc.stub.ClientCalls.blockingBidiStreamingCall(
getChannel(), getSetUpSessionMethod(), getCallOptions());
}
}
/**
* A stub to allow clients to do limited synchronous rpc calls to service S2AService.
*/
public static final class S2AServiceBlockingStub
extends io.grpc.stub.AbstractBlockingStub<S2AServiceBlockingStub> {
private S2AServiceBlockingStub(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
super(channel, callOptions);
}
@java.lang.Override
protected S2AServiceBlockingStub build(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new S2AServiceBlockingStub(channel, callOptions);
}
}
/**
* A stub to allow clients to do ListenableFuture-style rpc calls to service S2AService.
*/
public static final class S2AServiceFutureStub
extends io.grpc.stub.AbstractFutureStub<S2AServiceFutureStub> {
private S2AServiceFutureStub(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
super(channel, callOptions);
}
@java.lang.Override
protected S2AServiceFutureStub build(
io.grpc.Channel channel, io.grpc.CallOptions callOptions) {
return new S2AServiceFutureStub(channel, callOptions);
}
}
private static final int METHODID_SET_UP_SESSION = 0;
private static final class MethodHandlers<Req, Resp> implements
io.grpc.stub.ServerCalls.UnaryMethod<Req, Resp>,
io.grpc.stub.ServerCalls.ServerStreamingMethod<Req, Resp>,
io.grpc.stub.ServerCalls.ClientStreamingMethod<Req, Resp>,
io.grpc.stub.ServerCalls.BidiStreamingMethod<Req, Resp> {
private final AsyncService serviceImpl;
private final int methodId;
MethodHandlers(AsyncService serviceImpl, int methodId) {
this.serviceImpl = serviceImpl;
this.methodId = methodId;
}
@java.lang.Override
@java.lang.SuppressWarnings("unchecked")
public void invoke(Req request, io.grpc.stub.StreamObserver<Resp> responseObserver) {
switch (methodId) {
default:
throw new AssertionError();
}
}
@java.lang.Override
@java.lang.SuppressWarnings("unchecked")
public io.grpc.stub.StreamObserver<Req> invoke(
io.grpc.stub.StreamObserver<Resp> responseObserver) {
switch (methodId) {
case METHODID_SET_UP_SESSION:
return (io.grpc.stub.StreamObserver<Req>) serviceImpl.setUpSession(
(io.grpc.stub.StreamObserver<io.grpc.s2a.internal.handshaker.SessionResp>) responseObserver);
default:
throw new AssertionError();
}
}
}
public static final io.grpc.ServerServiceDefinition bindService(AsyncService service) {
return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor())
.addMethod(
getSetUpSessionMethod(),
io.grpc.stub.ServerCalls.asyncBidiStreamingCall(
new MethodHandlers<
io.grpc.s2a.internal.handshaker.SessionReq,
io.grpc.s2a.internal.handshaker.SessionResp>(
service, METHODID_SET_UP_SESSION)))
.build();
}
private static abstract class S2AServiceBaseDescriptorSupplier
implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier {
S2AServiceBaseDescriptorSupplier() {}
@java.lang.Override
public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() {
return io.grpc.s2a.internal.handshaker.S2AProto.getDescriptor();
}
@java.lang.Override
public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() {
return getFileDescriptor().findServiceByName("S2AService");
}
}
private static final class S2AServiceFileDescriptorSupplier
extends S2AServiceBaseDescriptorSupplier {
S2AServiceFileDescriptorSupplier() {}
}
private static final class S2AServiceMethodDescriptorSupplier
extends S2AServiceBaseDescriptorSupplier
implements io.grpc.protobuf.ProtoMethodDescriptorSupplier {
private final java.lang.String methodName;
S2AServiceMethodDescriptorSupplier(java.lang.String methodName) {
this.methodName = methodName;
}
@java.lang.Override
public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() {
return getServiceDescriptor().findMethodByName(methodName);
}
}
private static volatile io.grpc.ServiceDescriptor serviceDescriptor;
public static io.grpc.ServiceDescriptor getServiceDescriptor() {
io.grpc.ServiceDescriptor result = serviceDescriptor;
if (result == null) {
synchronized (S2AServiceGrpc.class) {
result = serviceDescriptor;
if (result == null) {
serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME)
.setSchemaDescriptor(new S2AServiceFileDescriptorSupplier())
.addMethod(getSetUpSessionMethod())
.build();
}
}
}
return result;
}
}

View File

@ -17,6 +17,7 @@
package io.grpc.s2a.internal.handshaker;
import com.google.errorprone.annotations.Immutable;
import com.google.s2a.proto.v2.AuthenticationMechanism;
import io.grpc.s2a.internal.handshaker.S2AIdentity;
import io.grpc.s2a.internal.handshaker.tokenmanager.AccessTokenManager;
import java.util.Optional;

View File

@ -18,6 +18,7 @@ package io.grpc.s2a.internal.handshaker;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.s2a.proto.v2.TLSVersion;
/** Converts proto messages to Netty strings. */
final class ProtoUtil {

View File

@ -19,6 +19,7 @@ package io.grpc.s2a.internal.handshaker;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.errorprone.annotations.ThreadSafe;
import com.google.s2a.proto.v2.Identity;
/**
* Stores an identity in such a way that it can be sent to the S2A handshaker service. The identity

View File

@ -22,6 +22,10 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableMap;
import com.google.protobuf.ByteString;
import com.google.s2a.proto.v2.OffloadPrivateKeyOperationReq;
import com.google.s2a.proto.v2.SessionReq;
import com.google.s2a.proto.v2.SessionResp;
import com.google.s2a.proto.v2.SignatureAlgorithm;
import io.grpc.s2a.internal.handshaker.S2AIdentity;
import io.netty.handler.ssl.OpenSslPrivateKeyMethod;
import java.io.IOException;

View File

@ -28,6 +28,7 @@ import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.errorprone.annotations.ThreadSafe;
import com.google.s2a.proto.v2.S2AServiceGrpc;
import io.grpc.Channel;
import io.grpc.internal.GrpcUtil;
import io.grpc.internal.ObjectPool;

View File

@ -22,6 +22,9 @@ import static com.google.common.base.Verify.verify;
import static java.util.concurrent.TimeUnit.SECONDS;
import com.google.common.annotations.VisibleForTesting;
import com.google.s2a.proto.v2.S2AServiceGrpc;
import com.google.s2a.proto.v2.SessionReq;
import com.google.s2a.proto.v2.SessionResp;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
import java.util.Optional;

View File

@ -21,8 +21,12 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.ByteString;
import com.google.s2a.proto.v2.SessionReq;
import com.google.s2a.proto.v2.SessionResp;
import com.google.s2a.proto.v2.ValidatePeerCertificateChainReq;
import com.google.s2a.proto.v2.ValidatePeerCertificateChainReq.VerificationMode;
import com.google.s2a.proto.v2.ValidatePeerCertificateChainResp;
import io.grpc.s2a.internal.handshaker.S2AIdentity;
import io.grpc.s2a.internal.handshaker.ValidatePeerCertificateChainReq.VerificationMode;
import java.io.IOException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;

View File

@ -20,6 +20,12 @@ import static com.google.common.base.Preconditions.checkNotNull;
import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.collect.ImmutableSet;
import com.google.s2a.proto.v2.AuthenticationMechanism;
import com.google.s2a.proto.v2.ConnectionSide;
import com.google.s2a.proto.v2.GetTlsConfigurationReq;
import com.google.s2a.proto.v2.GetTlsConfigurationResp;
import com.google.s2a.proto.v2.SessionReq;
import com.google.s2a.proto.v2.SessionResp;
import io.grpc.netty.GrpcSslContexts;
import io.grpc.s2a.internal.handshaker.S2AIdentity;
import io.netty.handler.ssl.OpenSslContextOption;

View File

@ -1,82 +0,0 @@
// Copyright 2024 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The canonical version of this proto can be found at
// https://github.com/grpc/grpc-proto/blob/master/grpc/gcp/s2a/common.proto
syntax = "proto3";
package grpc.gcp.s2a;
option java_multiple_files = true;
option java_outer_classname = "CommonProto";
option java_package = "io.grpc.s2a.internal.handshaker";
// The TLS 1.0-1.2 ciphersuites that the application can negotiate when using
// S2A.
enum Ciphersuite {
CIPHERSUITE_UNSPECIFIED = 0;
CIPHERSUITE_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 1;
CIPHERSUITE_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 2;
CIPHERSUITE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 3;
CIPHERSUITE_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 4;
CIPHERSUITE_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 5;
CIPHERSUITE_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 6;
}
// The TLS versions supported by S2A's handshaker module.
enum TLSVersion {
TLS_VERSION_UNSPECIFIED = 0;
TLS_VERSION_1_0 = 1;
TLS_VERSION_1_1 = 2;
TLS_VERSION_1_2 = 3;
TLS_VERSION_1_3 = 4;
}
// The side in the TLS connection.
enum ConnectionSide {
CONNECTION_SIDE_UNSPECIFIED = 0;
CONNECTION_SIDE_CLIENT = 1;
CONNECTION_SIDE_SERVER = 2;
}
// The ALPN protocols that the application can negotiate during a TLS handshake.
enum AlpnProtocol {
ALPN_PROTOCOL_UNSPECIFIED = 0;
ALPN_PROTOCOL_GRPC = 1;
ALPN_PROTOCOL_HTTP2 = 2;
ALPN_PROTOCOL_HTTP1_1 = 3;
}
message Identity {
oneof identity_oneof {
// The SPIFFE ID of a connection endpoint.
string spiffe_id = 1;
// The hostname of a connection endpoint.
string hostname = 2;
// The UID of a connection endpoint.
string uid = 4;
// The username of a connection endpoint.
string username = 5;
// The GCP ID of a connection endpoint.
string gcp_id = 6;
}
// Additional identity-specific attributes.
map<string, string> attributes = 3;
}

View File

@ -1,369 +0,0 @@
// Copyright 2024 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The canonical version of this proto can be found at
// https://github.com/grpc/grpc-proto/blob/master/grpc/gcp/s2a/s2a.proto
syntax = "proto3";
package grpc.gcp.s2a;
import "grpc/gcp/s2a/common.proto";
import "grpc/gcp/s2a/s2a_context.proto";
option java_multiple_files = true;
option java_outer_classname = "S2AProto";
option java_package = "io.grpc.s2a.internal.handshaker";
enum SignatureAlgorithm {
S2A_SSL_SIGN_UNSPECIFIED = 0;
// RSA Public-Key Cryptography Standards #1.
S2A_SSL_SIGN_RSA_PKCS1_SHA256 = 1;
S2A_SSL_SIGN_RSA_PKCS1_SHA384 = 2;
S2A_SSL_SIGN_RSA_PKCS1_SHA512 = 3;
// ECDSA.
S2A_SSL_SIGN_ECDSA_SECP256R1_SHA256 = 4;
S2A_SSL_SIGN_ECDSA_SECP384R1_SHA384 = 5;
S2A_SSL_SIGN_ECDSA_SECP521R1_SHA512 = 6;
// RSA Probabilistic Signature Scheme.
S2A_SSL_SIGN_RSA_PSS_RSAE_SHA256 = 7;
S2A_SSL_SIGN_RSA_PSS_RSAE_SHA384 = 8;
S2A_SSL_SIGN_RSA_PSS_RSAE_SHA512 = 9;
// ED25519.
S2A_SSL_SIGN_ED25519 = 10;
}
message AlpnPolicy {
// If true, the application MUST perform ALPN negotiation.
bool enable_alpn_negotiation = 1;
// The ordered list of ALPN protocols that specify how the application SHOULD
// negotiate ALPN during the TLS handshake.
//
// The application MAY ignore any ALPN protocols in this list that are not
// supported by the application.
repeated AlpnProtocol alpn_protocols = 2;
}
message AuthenticationMechanism {
// Applications may specify an identity associated to an authentication
// mechanism. Otherwise, S2A assumes that the authentication mechanism is
// associated with the default identity. If the default identity cannot be
// determined, the request is rejected.
Identity identity = 1;
oneof mechanism_oneof {
// A token that the application uses to authenticate itself to S2A.
string token = 2;
}
}
message Status {
// The status code that is specific to the application and the implementation
// of S2A, e.g., gRPC status code.
uint32 code = 1;
// The status details.
string details = 2;
}
message GetTlsConfigurationReq {
// The role of the application in the TLS connection.
ConnectionSide connection_side = 1;
// The server name indication (SNI) extension, which MAY be populated when a
// server is offloading to S2A. The SNI is used to determine the server
// identity if the local identity in the request is empty.
string sni = 2;
}
message GetTlsConfigurationResp {
// Next ID: 8
message ClientTlsConfiguration {
reserved 4, 5;
// The certificate chain that the client MUST use for the TLS handshake.
// It's a list of PEM-encoded certificates, ordered from leaf to root,
// excluding the root.
repeated string certificate_chain = 1;
// The minimum TLS version number that the client MUST use for the TLS
// handshake. If this field is not provided, the client MUST use the default
// minimum version of the client's TLS library.
TLSVersion min_tls_version = 2;
// The maximum TLS version number that the client MUST use for the TLS
// handshake. If this field is not provided, the client MUST use the default
// maximum version of the client's TLS library.
TLSVersion max_tls_version = 3;
// The ordered list of TLS 1.0-1.2 ciphersuites that the client MAY offer to
// negotiate in the TLS handshake.
repeated Ciphersuite ciphersuites = 6;
// The policy that dictates how the client negotiates ALPN during the TLS
// handshake.
AlpnPolicy alpn_policy = 7;
}
// Next ID: 12
message ServerTlsConfiguration {
reserved 4, 5;
enum RequestClientCertificate {
UNSPECIFIED = 0;
DONT_REQUEST_CLIENT_CERTIFICATE = 1;
REQUEST_CLIENT_CERTIFICATE_BUT_DONT_VERIFY = 2;
REQUEST_CLIENT_CERTIFICATE_AND_VERIFY = 3;
REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_BUT_DONT_VERIFY = 4;
REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY = 5;
}
// The certificate chain that the server MUST use for the TLS handshake.
// It's a list of PEM-encoded certificates, ordered from leaf to root,
// excluding the root.
repeated string certificate_chain = 1;
// The minimum TLS version number that the server MUST use for the TLS
// handshake. If this field is not provided, the server MUST use the default
// minimum version of the server's TLS library.
TLSVersion min_tls_version = 2;
// The maximum TLS version number that the server MUST use for the TLS
// handshake. If this field is not provided, the server MUST use the default
// maximum version of the server's TLS library.
TLSVersion max_tls_version = 3;
// The ordered list of TLS 1.0-1.2 ciphersuites that the server MAY offer to
// negotiate in the TLS handshake.
repeated Ciphersuite ciphersuites = 10;
// Whether to enable TLS resumption.
bool tls_resumption_enabled = 6;
// Whether the server MUST request a client certificate (i.e. to negotiate
// TLS vs. mTLS).
RequestClientCertificate request_client_certificate = 7;
// Returns the maximum number of extra bytes that
// |OffloadResumptionKeyOperation| can add to the number of unencrypted
// bytes to form the encrypted bytes.
uint32 max_overhead_of_ticket_aead = 9;
// The policy that dictates how the server negotiates ALPN during the TLS
// handshake.
AlpnPolicy alpn_policy = 11;
}
oneof tls_configuration {
ClientTlsConfiguration client_tls_configuration = 1;
ServerTlsConfiguration server_tls_configuration = 2;
}
}
message OffloadPrivateKeyOperationReq {
enum PrivateKeyOperation {
UNSPECIFIED = 0;
// When performing a TLS 1.2 or 1.3 handshake, the (partial) transcript of
// the TLS handshake must be signed to prove possession of the private key.
//
// See https://www.rfc-editor.org/rfc/rfc8446.html#section-4.4.3.
SIGN = 1;
// When performing a TLS 1.2 handshake using an RSA algorithm, the key
// exchange algorithm involves the client generating a premaster secret,
// encrypting it using the server's public key, and sending this encrypted
// blob to the server in a ClientKeyExchange message.
//
// See https://www.rfc-editor.org/rfc/rfc4346#section-7.4.7.1.
DECRYPT = 2;
}
// The operation the private key is used for.
PrivateKeyOperation operation = 1;
// The signature algorithm to be used for signing operations.
SignatureAlgorithm signature_algorithm = 2;
// The input bytes to be signed or decrypted.
oneof in_bytes {
// Raw bytes to be hashed and signed, or decrypted.
bytes raw_bytes = 4;
// A SHA256 hash to be signed. Must be 32 bytes.
bytes sha256_digest = 5;
// A SHA384 hash to be signed. Must be 48 bytes.
bytes sha384_digest = 6;
// A SHA512 hash to be signed. Must be 64 bytes.
bytes sha512_digest = 7;
}
}
message OffloadPrivateKeyOperationResp {
// The signed or decrypted output bytes.
bytes out_bytes = 1;
}
message OffloadResumptionKeyOperationReq {
enum ResumptionKeyOperation {
UNSPECIFIED = 0;
ENCRYPT = 1;
DECRYPT = 2;
}
// The operation the resumption key is used for.
ResumptionKeyOperation operation = 1;
// The bytes to be encrypted or decrypted.
bytes in_bytes = 2;
}
message OffloadResumptionKeyOperationResp {
// The encrypted or decrypted bytes.
bytes out_bytes = 1;
}
message ValidatePeerCertificateChainReq {
enum VerificationMode {
// The default verification mode supported by S2A.
UNSPECIFIED = 0;
// The SPIFFE verification mode selects the set of trusted certificates to
// use for path building based on the SPIFFE trust domain in the peer's leaf
// certificate.
SPIFFE = 1;
// The connect-to-Google verification mode uses the trust bundle for
// connecting to Google, e.g. *.mtls.googleapis.com endpoints.
CONNECT_TO_GOOGLE = 2;
}
message ClientPeer {
// The certificate chain to be verified. The chain MUST be a list of
// DER-encoded certificates, ordered from leaf to root, excluding the root.
repeated bytes certificate_chain = 1;
}
message ServerPeer {
// The certificate chain to be verified. The chain MUST be a list of
// DER-encoded certificates, ordered from leaf to root, excluding the root.
repeated bytes certificate_chain = 1;
// The expected hostname of the server.
string server_hostname = 2;
// The UnrestrictedClientPolicy specified by the user.
bytes serialized_unrestricted_client_policy = 3;
}
// The verification mode that S2A MUST use to validate the peer certificate
// chain.
VerificationMode mode = 1;
oneof peer_oneof {
ClientPeer client_peer = 2;
ServerPeer server_peer = 3;
}
}
message ValidatePeerCertificateChainResp {
enum ValidationResult {
UNSPECIFIED = 0;
SUCCESS = 1;
FAILURE = 2;
}
// The result of validating the peer certificate chain.
ValidationResult validation_result = 1;
// The validation details. This field is only populated when the validation
// result is NOT SUCCESS.
string validation_details = 2;
// The S2A context contains information from the peer certificate chain.
//
// The S2A context MAY be populated even if validation of the peer certificate
// chain fails.
S2AContext context = 3;
}
message SessionReq {
// The identity corresponding to the TLS configurations that MUST be used for
// the TLS handshake.
//
// If a managed identity already exists, the local identity and authentication
// mechanisms are ignored. If a managed identity doesn't exist and the local
// identity is not populated, S2A will try to deduce the managed identity to
// use from the SNI extension. If that also fails, S2A uses the default
// identity (if one exists).
Identity local_identity = 1;
// The authentication mechanisms that the application wishes to use to
// authenticate to S2A, ordered by preference. S2A will always use the first
// authentication mechanism that matches the managed identity.
repeated AuthenticationMechanism authentication_mechanisms = 2;
oneof req_oneof {
// Requests the certificate chain and TLS configuration corresponding to the
// local identity, which the application MUST use to negotiate the TLS
// handshake.
GetTlsConfigurationReq get_tls_configuration_req = 3;
// Signs or decrypts the input bytes using a private key corresponding to
// the local identity in the request.
//
// WARNING: More than one OffloadPrivateKeyOperationReq may be sent to the
// S2Av2 by a server during a TLS 1.2 handshake.
OffloadPrivateKeyOperationReq offload_private_key_operation_req = 4;
// Encrypts or decrypts the input bytes using a resumption key corresponding
// to the local identity in the request.
OffloadResumptionKeyOperationReq offload_resumption_key_operation_req = 5;
// Verifies the peer's certificate chain using
// (a) trust bundles corresponding to the local identity in the request, and
// (b) the verification mode in the request.
ValidatePeerCertificateChainReq validate_peer_certificate_chain_req = 6;
}
}
message SessionResp {
// Status of the session response.
//
// The status field is populated so that if an error occurs when making an
// individual request, then communication with the S2A may continue. If an
// error is returned directly (e.g. at the gRPC layer), then it may result
// that the bidirectional stream being closed.
Status status = 1;
oneof resp_oneof {
// Contains the certificate chain and TLS configurations corresponding to
// the local identity.
GetTlsConfigurationResp get_tls_configuration_resp = 2;
// Contains the signed or encrypted output bytes using the private key
// corresponding to the local identity.
OffloadPrivateKeyOperationResp offload_private_key_operation_resp = 3;
// Contains the encrypted or decrypted output bytes using the resumption key
// corresponding to the local identity.
OffloadResumptionKeyOperationResp offload_resumption_key_operation_resp = 4;
// Contains the validation result, peer identity and fingerprints of peer
// certificates.
ValidatePeerCertificateChainResp validate_peer_certificate_chain_resp = 5;
}
}
service S2AService {
// SetUpSession is a bidirectional stream used by applications to offload
// operations from the TLS handshake.
rpc SetUpSession(stream SessionReq) returns (stream SessionResp) {}
}

View File

@ -1,62 +0,0 @@
// Copyright 2024 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// The canonical version of this proto can be found at
// https://github.com/grpc/grpc-proto/blob/master/grpc/gcp/s2a/s2a_context.proto
syntax = "proto3";
package grpc.gcp.s2a;
import "grpc/gcp/s2a/common.proto";
option java_multiple_files = true;
option java_outer_classname = "S2AContextProto";
option java_package = "io.grpc.s2a.internal.handshaker";
message S2AContext {
// The SPIFFE ID from the peer leaf certificate, if present.
//
// This field is only populated if the leaf certificate is a valid SPIFFE
// SVID; in particular, there is a unique URI SAN and this URI SAN is a valid
// SPIFFE ID.
string leaf_cert_spiffe_id = 1;
// The URIs that are present in the SubjectAltName extension of the peer leaf
// certificate.
//
// Note that the extracted URIs are not validated and may not be properly
// formatted.
repeated string leaf_cert_uris = 2;
// The DNSNames that are present in the SubjectAltName extension of the peer
// leaf certificate.
repeated string leaf_cert_dnsnames = 3;
// The (ordered) list of fingerprints in the certificate chain used to verify
// the given leaf certificate. The order MUST be from leaf certificate
// fingerprint to root certificate fingerprint.
//
// A fingerprint is the base-64 encoding of the SHA256 hash of the
// DER-encoding of a certificate. The list MAY be populated even if the peer
// certificate chain was NOT validated successfully.
repeated string peer_certificate_chain_fingerprints = 4;
// The local identity used during session setup.
Identity local_identity = 5;
// The SHA256 hash of the DER-encoding of the local leaf certificate used in
// the handshake.
bytes local_leaf_cert_fingerprint = 6;
}

View File

@ -19,6 +19,7 @@ package io.grpc.s2a;
import static com.google.common.truth.Truth.assertThat;
import static java.util.concurrent.TimeUnit.SECONDS;
import com.google.s2a.proto.v2.S2AServiceGrpc;
import io.grpc.Channel;
import io.grpc.ChannelCredentials;
import io.grpc.Grpc;
@ -37,7 +38,6 @@ import io.grpc.netty.NettyServerBuilder;
import io.grpc.s2a.S2AChannelCredentials;
import io.grpc.s2a.internal.channel.S2AHandshakerServiceChannel;
import io.grpc.s2a.internal.handshaker.FakeS2AServer;
import io.grpc.s2a.internal.handshaker.S2AServiceGrpc;
import io.grpc.s2a.internal.handshaker.S2AStub;
import io.grpc.stub.StreamObserver;
import io.grpc.testing.protobuf.SimpleRequest;

View File

@ -16,6 +16,9 @@
package io.grpc.s2a.internal.handshaker;
import com.google.s2a.proto.v2.S2AServiceGrpc;
import com.google.s2a.proto.v2.SessionReq;
import com.google.s2a.proto.v2.SessionResp;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;

View File

@ -22,12 +22,22 @@ import static java.util.concurrent.TimeUnit.SECONDS;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.SettableFuture;
import com.google.protobuf.ByteString;
import com.google.s2a.proto.v2.Ciphersuite;
import com.google.s2a.proto.v2.ConnectionSide;
import com.google.s2a.proto.v2.GetTlsConfigurationReq;
import com.google.s2a.proto.v2.GetTlsConfigurationResp;
import com.google.s2a.proto.v2.S2AServiceGrpc;
import com.google.s2a.proto.v2.SessionReq;
import com.google.s2a.proto.v2.SessionResp;
import com.google.s2a.proto.v2.TLSVersion;
import com.google.s2a.proto.v2.ValidatePeerCertificateChainReq;
import com.google.s2a.proto.v2.ValidatePeerCertificateChainReq.VerificationMode;
import com.google.s2a.proto.v2.ValidatePeerCertificateChainResp;
import io.grpc.Grpc;
import io.grpc.InsecureChannelCredentials;
import io.grpc.ManagedChannel;
import io.grpc.Server;
import io.grpc.ServerBuilder;
import io.grpc.s2a.internal.handshaker.ValidatePeerCertificateChainReq.VerificationMode;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
import java.io.InputStream;

View File

@ -16,13 +16,25 @@
package io.grpc.s2a.internal.handshaker;
import static io.grpc.s2a.internal.handshaker.TLSVersion.TLS_VERSION_1_2;
import static io.grpc.s2a.internal.handshaker.TLSVersion.TLS_VERSION_1_3;
import static com.google.s2a.proto.v2.TLSVersion.TLS_VERSION_1_2;
import static com.google.s2a.proto.v2.TLSVersion.TLS_VERSION_1_3;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.CharStreams;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.protobuf.ByteString;
import com.google.s2a.proto.v2.Ciphersuite;
import com.google.s2a.proto.v2.ConnectionSide;
import com.google.s2a.proto.v2.GetTlsConfigurationReq;
import com.google.s2a.proto.v2.GetTlsConfigurationResp;
import com.google.s2a.proto.v2.OffloadPrivateKeyOperationReq;
import com.google.s2a.proto.v2.OffloadPrivateKeyOperationResp;
import com.google.s2a.proto.v2.SessionReq;
import com.google.s2a.proto.v2.SessionResp;
import com.google.s2a.proto.v2.SignatureAlgorithm;
import com.google.s2a.proto.v2.Status;
import com.google.s2a.proto.v2.ValidatePeerCertificateChainReq;
import com.google.s2a.proto.v2.ValidatePeerCertificateChainResp;
import io.grpc.stub.StreamObserver;
import io.grpc.util.CertificateUtils;
import java.io.FileNotFoundException;

View File

@ -17,6 +17,7 @@
package io.grpc.s2a.internal.handshaker;
import com.google.common.truth.Expect;
import com.google.s2a.proto.v2.AuthenticationMechanism;
import io.grpc.s2a.internal.handshaker.S2AIdentity;
import io.grpc.s2a.internal.handshaker.tokenmanager.AccessTokenManager;
import io.grpc.s2a.internal.handshaker.tokenmanager.SingleTokenFetcher;

View File

@ -20,6 +20,7 @@ import static org.junit.Assert.assertThrows;
import com.google.common.collect.ImmutableSet;
import com.google.common.truth.Expect;
import com.google.s2a.proto.v2.TLSVersion;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

View File

@ -25,6 +25,11 @@ import static org.mockito.Mockito.when;
import com.google.common.truth.Expect;
import com.google.protobuf.ByteString;
import com.google.s2a.proto.v2.OffloadPrivateKeyOperationReq;
import com.google.s2a.proto.v2.OffloadPrivateKeyOperationResp;
import com.google.s2a.proto.v2.SessionReq;
import com.google.s2a.proto.v2.SessionResp;
import com.google.s2a.proto.v2.SignatureAlgorithm;
import io.grpc.netty.GrpcSslContexts;
import io.grpc.s2a.internal.handshaker.S2AIdentity;
import io.netty.handler.ssl.OpenSslPrivateKeyMethod;

View File

@ -22,6 +22,9 @@ import static org.mockito.Mockito.verify;
import com.google.common.testing.NullPointerTester;
import com.google.common.testing.NullPointerTester.Visibility;
import com.google.s2a.proto.v2.S2AServiceGrpc;
import com.google.s2a.proto.v2.SessionReq;
import com.google.s2a.proto.v2.SessionResp;
import io.grpc.Channel;
import io.grpc.InsecureChannelCredentials;
import io.grpc.Server;

View File

@ -21,6 +21,15 @@ import static com.google.common.truth.extensions.proto.ProtoTruth.assertThat;
import static org.junit.Assert.assertThrows;
import com.google.common.truth.Expect;
import com.google.s2a.proto.v2.Ciphersuite;
import com.google.s2a.proto.v2.ConnectionSide;
import com.google.s2a.proto.v2.GetTlsConfigurationReq;
import com.google.s2a.proto.v2.GetTlsConfigurationResp;
import com.google.s2a.proto.v2.S2AServiceGrpc;
import com.google.s2a.proto.v2.SessionReq;
import com.google.s2a.proto.v2.SessionResp;
import com.google.s2a.proto.v2.Status;
import com.google.s2a.proto.v2.TLSVersion;
import io.grpc.Channel;
import io.grpc.InsecureChannelCredentials;
import io.grpc.internal.ObjectPool;