Add ALTS code (#1865)
This commit is contained in:
286
credentials/alts/alts.go
Normal file
286
credentials/alts/alts.go
Normal file
@ -0,0 +1,286 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package alts implements the ALTS credential support by gRPC library, which
|
||||||
|
// encapsulates all the state needed by a client to authenticate with a server
|
||||||
|
// using ALTS and make various assertions, e.g., about the client's identity,
|
||||||
|
// role, or whether it is authorized to make a particular call.
|
||||||
|
// This package is experimental.
|
||||||
|
package alts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
"google.golang.org/grpc/credentials/alts/core"
|
||||||
|
"google.golang.org/grpc/credentials/alts/core/handshaker"
|
||||||
|
"google.golang.org/grpc/credentials/alts/core/handshaker/service"
|
||||||
|
altspb "google.golang.org/grpc/credentials/alts/core/proto"
|
||||||
|
"google.golang.org/grpc/grpclog"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// defaultTimeout specifies the server handshake timeout.
|
||||||
|
defaultTimeout = 30.0 * time.Second
|
||||||
|
// The following constants specify the minimum and maximum acceptable
|
||||||
|
// protocol versions.
|
||||||
|
protocolVersionMaxMajor = 2
|
||||||
|
protocolVersionMaxMinor = 1
|
||||||
|
protocolVersionMinMajor = 2
|
||||||
|
protocolVersionMinMinor = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
enableUntrustedALTS = flag.Bool("enable_untrusted_alts", false, "Enables ALTS in untrusted mode. Enabling this mode is risky since we cannot ensure that the application is running on GCP with a trusted handshaker service.")
|
||||||
|
once sync.Once
|
||||||
|
maxRPCVersion = &altspb.RpcProtocolVersions_Version{
|
||||||
|
Major: protocolVersionMaxMajor,
|
||||||
|
Minor: protocolVersionMaxMinor,
|
||||||
|
}
|
||||||
|
minRPCVersion = &altspb.RpcProtocolVersions_Version{
|
||||||
|
Major: protocolVersionMinMajor,
|
||||||
|
Minor: protocolVersionMinMinor,
|
||||||
|
}
|
||||||
|
// ErrUntrustedPlatform is returned from ClientHandshake and
|
||||||
|
// ServerHandshake is running on a platform where the trustworthiness of
|
||||||
|
// the handshaker service is not guaranteed.
|
||||||
|
ErrUntrustedPlatform = errors.New("untrusted platform, use enable_untrusted_alts flag at your own risk")
|
||||||
|
)
|
||||||
|
|
||||||
|
// AuthInfo exposes security information from the ALTS handshake to the
|
||||||
|
// application. This interface is to be implemented by ALTS. Users should not
|
||||||
|
// need a brand new implementation of this interface. For situations like
|
||||||
|
// testing, any new implementation should embed this interface. This allows
|
||||||
|
// ALTS to add new methods to this interface.
|
||||||
|
type AuthInfo interface {
|
||||||
|
// ApplicationProtocol returns application protocol negotiated for the
|
||||||
|
// ALTS connection.
|
||||||
|
ApplicationProtocol() string
|
||||||
|
// RecordProtocol returns the record protocol negotiated for the ALTS
|
||||||
|
// connection.
|
||||||
|
RecordProtocol() string
|
||||||
|
// SecurityLevel returns the security level of the created ALTS secure
|
||||||
|
// channel.
|
||||||
|
SecurityLevel() altspb.SecurityLevel
|
||||||
|
// PeerServiceAccount returns the peer service account.
|
||||||
|
PeerServiceAccount() string
|
||||||
|
// LocalServiceAccount returns the local service account.
|
||||||
|
LocalServiceAccount() string
|
||||||
|
// PeerRPCVersions returns the RPC version supported by the peer.
|
||||||
|
PeerRPCVersions() *altspb.RpcProtocolVersions
|
||||||
|
}
|
||||||
|
|
||||||
|
// altsTC is the credentials required for authenticating a connection using ALTS.
|
||||||
|
// It implements credentials.TransportCredentials interface.
|
||||||
|
type altsTC struct {
|
||||||
|
info *credentials.ProtocolInfo
|
||||||
|
hsAddr string
|
||||||
|
side core.Side
|
||||||
|
accounts []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientALTS constructs a client-side ALTS TransportCredentials object.
|
||||||
|
func NewClientALTS(targetServiceAccounts []string) credentials.TransportCredentials {
|
||||||
|
return newALTS(core.ClientSide, targetServiceAccounts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServerALTS constructs a server-side ALTS TransportCredentials object.
|
||||||
|
func NewServerALTS() credentials.TransportCredentials {
|
||||||
|
return newALTS(core.ServerSide, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newALTS(side core.Side, accounts []string) credentials.TransportCredentials {
|
||||||
|
// Make sure flags are parsed before accessing enableUntrustedALTS.
|
||||||
|
once.Do(func() {
|
||||||
|
flag.Parse()
|
||||||
|
vmOnGCP = isRunningOnGCP()
|
||||||
|
})
|
||||||
|
if *enableUntrustedALTS {
|
||||||
|
grpclog.Warning("untrusted ALTS mode is enabled and we cannot guarantee the trustworthiness of the ALTS handshaker service.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &altsTC{
|
||||||
|
info: &credentials.ProtocolInfo{
|
||||||
|
SecurityProtocol: "alts",
|
||||||
|
SecurityVersion: "1.0",
|
||||||
|
},
|
||||||
|
side: side,
|
||||||
|
accounts: accounts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientHandshake implements the client side handshake protocol.
|
||||||
|
func (g *altsTC) ClientHandshake(ctx context.Context, addr string, rawConn net.Conn) (_ net.Conn, _ credentials.AuthInfo, err error) {
|
||||||
|
if !*enableUntrustedALTS && !vmOnGCP {
|
||||||
|
return nil, nil, ErrUntrustedPlatform
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connecting to ALTS handshaker service.
|
||||||
|
hsConn, err := service.Dial()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
// Do not close hsConn since it is shared with other handshakes.
|
||||||
|
|
||||||
|
// Possible context leak:
|
||||||
|
// The cancel function for the child context we create will only be
|
||||||
|
// called a non-nil error is returned.
|
||||||
|
var cancel context.CancelFunc
|
||||||
|
ctx, cancel = context.WithCancel(ctx)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
opts := handshaker.DefaultClientHandshakerOptions()
|
||||||
|
opts.TargetServiceAccounts = g.accounts
|
||||||
|
opts.RPCVersions = &altspb.RpcProtocolVersions{
|
||||||
|
MaxRpcVersion: maxRPCVersion,
|
||||||
|
MinRpcVersion: minRPCVersion,
|
||||||
|
}
|
||||||
|
chs, err := handshaker.NewClientHandshaker(ctx, hsConn, rawConn, opts)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
chs.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
secConn, authInfo, err := chs.ClientHandshake(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
altsAuthInfo, ok := authInfo.(AuthInfo)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil, errors.New("client-side auth info is not of type alts.AuthInfo")
|
||||||
|
}
|
||||||
|
match, _ := checkRPCVersions(opts.RPCVersions, altsAuthInfo.PeerRPCVersions())
|
||||||
|
if !match {
|
||||||
|
return nil, nil, fmt.Errorf("server-side RPC versions are not compatible with this client, local versions: %v, peer versions: %v", opts.RPCVersions, altsAuthInfo.PeerRPCVersions())
|
||||||
|
}
|
||||||
|
return secConn, authInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandshake implements the server side ALTS handshaker.
|
||||||
|
func (g *altsTC) ServerHandshake(rawConn net.Conn) (_ net.Conn, _ credentials.AuthInfo, err error) {
|
||||||
|
if !*enableUntrustedALTS && !vmOnGCP {
|
||||||
|
return nil, nil, ErrUntrustedPlatform
|
||||||
|
}
|
||||||
|
// Connecting to ALTS handshaker service.
|
||||||
|
hsConn, err := service.Dial()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
// Do not close hsConn since it's shared with other handshakes.
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
|
||||||
|
defer cancel()
|
||||||
|
opts := handshaker.DefaultServerHandshakerOptions()
|
||||||
|
opts.RPCVersions = &altspb.RpcProtocolVersions{
|
||||||
|
MaxRpcVersion: maxRPCVersion,
|
||||||
|
MinRpcVersion: minRPCVersion,
|
||||||
|
}
|
||||||
|
shs, err := handshaker.NewServerHandshaker(ctx, hsConn, rawConn, opts)
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
shs.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
secConn, authInfo, err := shs.ServerHandshake(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
altsAuthInfo, ok := authInfo.(AuthInfo)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil, errors.New("server-side auth info is not of type alts.AuthInfo")
|
||||||
|
}
|
||||||
|
match, _ := checkRPCVersions(opts.RPCVersions, altsAuthInfo.PeerRPCVersions())
|
||||||
|
if !match {
|
||||||
|
return nil, nil, fmt.Errorf("client-side RPC versions is not compatible with this server, local versions: %v, peer versions: %v", opts.RPCVersions, altsAuthInfo.PeerRPCVersions())
|
||||||
|
}
|
||||||
|
return secConn, authInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *altsTC) Info() credentials.ProtocolInfo {
|
||||||
|
return *g.info
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *altsTC) Clone() credentials.TransportCredentials {
|
||||||
|
info := *g.info
|
||||||
|
return &altsTC{
|
||||||
|
info: &info,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *altsTC) OverrideServerName(serverNameOverride string) error {
|
||||||
|
g.info.ServerName = serverNameOverride
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// compareRPCVersion returns 0 if v1 == v2, 1 if v1 > v2 and -1 if v1 < v2.
|
||||||
|
func compareRPCVersions(v1, v2 *altspb.RpcProtocolVersions_Version) int {
|
||||||
|
switch {
|
||||||
|
case v1.GetMajor() > v2.GetMajor(),
|
||||||
|
v1.GetMajor() == v2.GetMajor() && v1.GetMinor() > v2.GetMinor():
|
||||||
|
return 1
|
||||||
|
case v1.GetMajor() < v2.GetMajor(),
|
||||||
|
v1.GetMajor() == v2.GetMajor() && v1.GetMinor() < v2.GetMinor():
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// checkRPCVersions performs a version check between local and peer rpc protocol
|
||||||
|
// versions. This function returns true if the check passes which means both
|
||||||
|
// parties agreed on a common rpc protocol to use, and false otherwise. The
|
||||||
|
// function also returns the highest common RPC protocol version both parties
|
||||||
|
// agreed on.
|
||||||
|
func checkRPCVersions(local, peer *altspb.RpcProtocolVersions) (bool, *altspb.RpcProtocolVersions_Version) {
|
||||||
|
if local == nil || peer == nil {
|
||||||
|
grpclog.Error("invalid checkRPCVersions argument, either local or peer is nil.")
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// maxCommonVersion is MIN(local.max, peer.max).
|
||||||
|
maxCommonVersion := local.GetMaxRpcVersion()
|
||||||
|
if compareRPCVersions(local.GetMaxRpcVersion(), peer.GetMaxRpcVersion()) > 0 {
|
||||||
|
maxCommonVersion = peer.GetMaxRpcVersion()
|
||||||
|
}
|
||||||
|
|
||||||
|
// minCommonVersion is MAX(local.min, peer.min).
|
||||||
|
minCommonVersion := peer.GetMinRpcVersion()
|
||||||
|
if compareRPCVersions(local.GetMinRpcVersion(), peer.GetMinRpcVersion()) > 0 {
|
||||||
|
minCommonVersion = local.GetMinRpcVersion()
|
||||||
|
}
|
||||||
|
|
||||||
|
if compareRPCVersions(maxCommonVersion, minCommonVersion) < 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return true, maxCommonVersion
|
||||||
|
}
|
246
credentials/alts/alts_test.go
Normal file
246
credentials/alts/alts_test.go
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package alts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
altspb "google.golang.org/grpc/credentials/alts/core/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInfoServerName(t *testing.T) {
|
||||||
|
// This is not testing any handshaker functionality, so it's fine to only
|
||||||
|
// use NewServerALTS and not NewClientALTS.
|
||||||
|
alts := NewServerALTS()
|
||||||
|
if got, want := alts.Info().ServerName, ""; got != want {
|
||||||
|
t.Fatalf("%v.Info().ServerName = %v, want %v", alts, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOverrideServerName(t *testing.T) {
|
||||||
|
wantServerName := "server.name"
|
||||||
|
// This is not testing any handshaker functionality, so it's fine to only
|
||||||
|
// use NewServerALTS and not NewClientALTS.
|
||||||
|
c := NewServerALTS()
|
||||||
|
c.OverrideServerName(wantServerName)
|
||||||
|
if got, want := c.Info().ServerName, wantServerName; got != want {
|
||||||
|
t.Fatalf("c.Info().ServerName = %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClone(t *testing.T) {
|
||||||
|
wantServerName := "server.name"
|
||||||
|
// This is not testing any handshaker functionality, so it's fine to only
|
||||||
|
// use NewServerALTS and not NewClientALTS.
|
||||||
|
c := NewServerALTS()
|
||||||
|
c.OverrideServerName(wantServerName)
|
||||||
|
cc := c.Clone()
|
||||||
|
if got, want := cc.Info().ServerName, wantServerName; got != want {
|
||||||
|
t.Fatalf("cc.Info().ServerName = %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
cc.OverrideServerName("")
|
||||||
|
if got, want := c.Info().ServerName, wantServerName; got != want {
|
||||||
|
t.Fatalf("Change in clone should not affect the original, c.Info().ServerName = %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := cc.Info().ServerName, ""; got != want {
|
||||||
|
t.Fatalf("cc.Info().ServerName = %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInfo(t *testing.T) {
|
||||||
|
// This is not testing any handshaker functionality, so it's fine to only
|
||||||
|
// use NewServerALTS and not NewClientALTS.
|
||||||
|
c := NewServerALTS()
|
||||||
|
info := c.Info()
|
||||||
|
if got, want := info.ProtocolVersion, ""; got != want {
|
||||||
|
t.Errorf("info.ProtocolVersion=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := info.SecurityProtocol, "alts"; got != want {
|
||||||
|
t.Errorf("info.SecurityProtocol=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := info.SecurityVersion, "1.0"; got != want {
|
||||||
|
t.Errorf("info.SecurityVersion=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := info.ServerName, ""; got != want {
|
||||||
|
t.Errorf("info.ServerName=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCompareRPCVersions(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
v1 *altspb.RpcProtocolVersions_Version
|
||||||
|
v2 *altspb.RpcProtocolVersions_Version
|
||||||
|
output int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
version(3, 2),
|
||||||
|
version(2, 1),
|
||||||
|
1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version(3, 2),
|
||||||
|
version(3, 1),
|
||||||
|
1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version(2, 1),
|
||||||
|
version(3, 2),
|
||||||
|
-1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version(3, 1),
|
||||||
|
version(3, 2),
|
||||||
|
-1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version(3, 2),
|
||||||
|
version(3, 2),
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
if got, want := compareRPCVersions(tc.v1, tc.v2), tc.output; got != want {
|
||||||
|
t.Errorf("compareRPCVersions(%v, %v)=%v, want %v", tc.v1, tc.v2, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckRPCVersions(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
desc string
|
||||||
|
local *altspb.RpcProtocolVersions
|
||||||
|
peer *altspb.RpcProtocolVersions
|
||||||
|
output bool
|
||||||
|
maxCommonVersion *altspb.RpcProtocolVersions_Version
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"local.max > peer.max and local.min > peer.min",
|
||||||
|
versions(2, 1, 3, 2),
|
||||||
|
versions(1, 2, 2, 1),
|
||||||
|
true,
|
||||||
|
version(2, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"local.max > peer.max and local.min < peer.min",
|
||||||
|
versions(1, 2, 3, 2),
|
||||||
|
versions(2, 1, 2, 1),
|
||||||
|
true,
|
||||||
|
version(2, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"local.max > peer.max and local.min = peer.min",
|
||||||
|
versions(2, 1, 3, 2),
|
||||||
|
versions(2, 1, 2, 1),
|
||||||
|
true,
|
||||||
|
version(2, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"local.max < peer.max and local.min > peer.min",
|
||||||
|
versions(2, 1, 2, 1),
|
||||||
|
versions(1, 2, 3, 2),
|
||||||
|
true,
|
||||||
|
version(2, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"local.max = peer.max and local.min > peer.min",
|
||||||
|
versions(2, 1, 2, 1),
|
||||||
|
versions(1, 2, 2, 1),
|
||||||
|
true,
|
||||||
|
version(2, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"local.max < peer.max and local.min < peer.min",
|
||||||
|
versions(1, 2, 2, 1),
|
||||||
|
versions(2, 1, 3, 2),
|
||||||
|
true,
|
||||||
|
version(2, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"local.max < peer.max and local.min = peer.min",
|
||||||
|
versions(1, 2, 2, 1),
|
||||||
|
versions(1, 2, 3, 2),
|
||||||
|
true,
|
||||||
|
version(2, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"local.max = peer.max and local.min < peer.min",
|
||||||
|
versions(1, 2, 2, 1),
|
||||||
|
versions(2, 1, 2, 1),
|
||||||
|
true,
|
||||||
|
version(2, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"all equal",
|
||||||
|
versions(2, 1, 2, 1),
|
||||||
|
versions(2, 1, 2, 1),
|
||||||
|
true,
|
||||||
|
version(2, 1),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"max is smaller than min",
|
||||||
|
versions(2, 1, 1, 2),
|
||||||
|
versions(2, 1, 1, 2),
|
||||||
|
false,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no overlap, local > peer",
|
||||||
|
versions(4, 3, 6, 5),
|
||||||
|
versions(1, 0, 2, 1),
|
||||||
|
false,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no overlap, local < peer",
|
||||||
|
versions(1, 0, 2, 1),
|
||||||
|
versions(4, 3, 6, 5),
|
||||||
|
false,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no overlap, max < min",
|
||||||
|
versions(6, 5, 4, 3),
|
||||||
|
versions(2, 1, 1, 0),
|
||||||
|
false,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
output, maxCommonVersion := checkRPCVersions(tc.local, tc.peer)
|
||||||
|
if got, want := output, tc.output; got != want {
|
||||||
|
t.Errorf("%v: checkRPCVersions(%v, %v)=(%v, _), want (%v, _)", tc.desc, tc.local, tc.peer, got, want)
|
||||||
|
}
|
||||||
|
if got, want := maxCommonVersion, tc.maxCommonVersion; !proto.Equal(got, want) {
|
||||||
|
t.Errorf("%v: checkRPCVersions(%v, %v)=(_, %v), want (_, %v)", tc.desc, tc.local, tc.peer, got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func version(major, minor uint32) *altspb.RpcProtocolVersions_Version {
|
||||||
|
return &altspb.RpcProtocolVersions_Version{
|
||||||
|
Major: major,
|
||||||
|
Minor: minor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func versions(minMajor, minMinor, maxMajor, maxMinor uint32) *altspb.RpcProtocolVersions {
|
||||||
|
return &altspb.RpcProtocolVersions{
|
||||||
|
MinRpcVersion: version(minMajor, minMinor),
|
||||||
|
MaxRpcVersion: version(maxMajor, maxMinor),
|
||||||
|
}
|
||||||
|
}
|
87
credentials/alts/core/authinfo/authinfo.go
Normal file
87
credentials/alts/core/authinfo/authinfo.go
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package authinfo provide authentication information returned by handshakers.
|
||||||
|
package authinfo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
altspb "google.golang.org/grpc/credentials/alts/core/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ credentials.AuthInfo = (*altsAuthInfo)(nil)
|
||||||
|
|
||||||
|
// altsAuthInfo exposes security information from the ALTS handshake to the
|
||||||
|
// application. altsAuthInfo is immutable and implements credentials.AuthInfo.
|
||||||
|
type altsAuthInfo struct {
|
||||||
|
p *altspb.AltsContext
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new altsAuthInfo object given handshaker results.
|
||||||
|
func New(result *altspb.HandshakerResult) credentials.AuthInfo {
|
||||||
|
return newAuthInfo(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAuthInfo(result *altspb.HandshakerResult) *altsAuthInfo {
|
||||||
|
return &altsAuthInfo{
|
||||||
|
p: &altspb.AltsContext{
|
||||||
|
ApplicationProtocol: result.GetApplicationProtocol(),
|
||||||
|
RecordProtocol: result.GetRecordProtocol(),
|
||||||
|
// TODO: assign security level from result.
|
||||||
|
SecurityLevel: altspb.SecurityLevel_INTEGRITY_AND_PRIVACY,
|
||||||
|
PeerServiceAccount: result.GetPeerIdentity().GetServiceAccount(),
|
||||||
|
LocalServiceAccount: result.GetLocalIdentity().GetServiceAccount(),
|
||||||
|
PeerRpcVersions: result.GetPeerRpcVersions(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthType identifies the context as providing ALTS authentication information.
|
||||||
|
func (s *altsAuthInfo) AuthType() string {
|
||||||
|
return "alts"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ApplicationProtocol returns the context's application protocol.
|
||||||
|
func (s *altsAuthInfo) ApplicationProtocol() string {
|
||||||
|
return s.p.GetApplicationProtocol()
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecordProtocol returns the context's record protocol.
|
||||||
|
func (s *altsAuthInfo) RecordProtocol() string {
|
||||||
|
return s.p.GetRecordProtocol()
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecurityLevel returns the context's security level.
|
||||||
|
func (s *altsAuthInfo) SecurityLevel() altspb.SecurityLevel {
|
||||||
|
return s.p.GetSecurityLevel()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeerServiceAccount returns the context's peer service account.
|
||||||
|
func (s *altsAuthInfo) PeerServiceAccount() string {
|
||||||
|
return s.p.GetPeerServiceAccount()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalServiceAccount returns the context's local service account.
|
||||||
|
func (s *altsAuthInfo) LocalServiceAccount() string {
|
||||||
|
return s.p.GetLocalServiceAccount()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PeerRPCVersions returns the context's peer RPC versions.
|
||||||
|
func (s *altsAuthInfo) PeerRPCVersions() *altspb.RpcProtocolVersions {
|
||||||
|
return s.p.GetPeerRpcVersions()
|
||||||
|
}
|
134
credentials/alts/core/authinfo/authinfo_test.go
Normal file
134
credentials/alts/core/authinfo/authinfo_test.go
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package authinfo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
altspb "google.golang.org/grpc/credentials/alts/core/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testAppProtocol = "my_app"
|
||||||
|
testRecordProtocol = "very_secure_protocol"
|
||||||
|
testPeerAccount = "peer_service_account"
|
||||||
|
testLocalAccount = "local_service_account"
|
||||||
|
testPeerHostname = "peer_hostname"
|
||||||
|
testLocalHostname = "local_hostname"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestALTSAuthInfo(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
result *altspb.HandshakerResult
|
||||||
|
outAppProtocol string
|
||||||
|
outRecordProtocol string
|
||||||
|
outSecurityLevel altspb.SecurityLevel
|
||||||
|
outPeerAccount string
|
||||||
|
outLocalAccount string
|
||||||
|
outPeerRPCVersions *altspb.RpcProtocolVersions
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
&altspb.HandshakerResult{
|
||||||
|
ApplicationProtocol: testAppProtocol,
|
||||||
|
RecordProtocol: testRecordProtocol,
|
||||||
|
PeerIdentity: &altspb.Identity{
|
||||||
|
IdentityOneof: &altspb.Identity_ServiceAccount{
|
||||||
|
ServiceAccount: testPeerAccount,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
LocalIdentity: &altspb.Identity{
|
||||||
|
IdentityOneof: &altspb.Identity_ServiceAccount{
|
||||||
|
ServiceAccount: testLocalAccount,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAppProtocol,
|
||||||
|
testRecordProtocol,
|
||||||
|
altspb.SecurityLevel_INTEGRITY_AND_PRIVACY,
|
||||||
|
testPeerAccount,
|
||||||
|
testLocalAccount,
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
&altspb.HandshakerResult{
|
||||||
|
ApplicationProtocol: testAppProtocol,
|
||||||
|
RecordProtocol: testRecordProtocol,
|
||||||
|
PeerIdentity: &altspb.Identity{
|
||||||
|
IdentityOneof: &altspb.Identity_Hostname{
|
||||||
|
Hostname: testPeerHostname,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
LocalIdentity: &altspb.Identity{
|
||||||
|
IdentityOneof: &altspb.Identity_Hostname{
|
||||||
|
Hostname: testLocalHostname,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
PeerRpcVersions: &altspb.RpcProtocolVersions{
|
||||||
|
MaxRpcVersion: &altspb.RpcProtocolVersions_Version{
|
||||||
|
Major: 20,
|
||||||
|
Minor: 21,
|
||||||
|
},
|
||||||
|
MinRpcVersion: &altspb.RpcProtocolVersions_Version{
|
||||||
|
Major: 10,
|
||||||
|
Minor: 11,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
testAppProtocol,
|
||||||
|
testRecordProtocol,
|
||||||
|
altspb.SecurityLevel_INTEGRITY_AND_PRIVACY,
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
&altspb.RpcProtocolVersions{
|
||||||
|
MaxRpcVersion: &altspb.RpcProtocolVersions_Version{
|
||||||
|
Major: 20,
|
||||||
|
Minor: 21,
|
||||||
|
},
|
||||||
|
MinRpcVersion: &altspb.RpcProtocolVersions_Version{
|
||||||
|
Major: 10,
|
||||||
|
Minor: 11,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
authInfo := newAuthInfo(tc.result)
|
||||||
|
if got, want := authInfo.AuthType(), "alts"; got != want {
|
||||||
|
t.Errorf("authInfo.AuthType()=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := authInfo.ApplicationProtocol(), tc.outAppProtocol; got != want {
|
||||||
|
t.Errorf("authInfo.ApplicationProtocol()=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := authInfo.RecordProtocol(), tc.outRecordProtocol; got != want {
|
||||||
|
t.Errorf("authInfo.RecordProtocol()=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := authInfo.SecurityLevel(), tc.outSecurityLevel; got != want {
|
||||||
|
t.Errorf("authInfo.SecurityLevel()=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := authInfo.PeerServiceAccount(), tc.outPeerAccount; got != want {
|
||||||
|
t.Errorf("authInfo.PeerServiceAccount()=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := authInfo.LocalServiceAccount(), tc.outLocalAccount; got != want {
|
||||||
|
t.Errorf("authInfo.LocalServiceAccount()=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
if got, want := authInfo.PeerRPCVersions(), tc.outPeerRPCVersions; !reflect.DeepEqual(got, want) {
|
||||||
|
t.Errorf("authinfo.PeerRpcVersions()=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
68
credentials/alts/core/common.go
Normal file
68
credentials/alts/core/common.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package core contains common core functionality for ALTS.
|
||||||
|
// Disclaimer: users should NEVER reference this package directly.
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// ClientSide identifies the client in this communication.
|
||||||
|
ClientSide Side = iota
|
||||||
|
// ServerSide identifies the server in this communication.
|
||||||
|
ServerSide
|
||||||
|
)
|
||||||
|
|
||||||
|
// PeerNotRespondingError is returned when a peer server is not responding
|
||||||
|
// after a channel has been established. It is treated as a temporary connection
|
||||||
|
// error and re-connection to the server should be attempted.
|
||||||
|
var PeerNotRespondingError = &peerNotRespondingError{}
|
||||||
|
|
||||||
|
// Side identifies the party's role: client or server.
|
||||||
|
type Side int
|
||||||
|
|
||||||
|
type peerNotRespondingError struct{}
|
||||||
|
|
||||||
|
// Return an error message for the purpose of logging.
|
||||||
|
func (e *peerNotRespondingError) Error() string {
|
||||||
|
return "peer server is not responding and re-connection should be attempted."
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary indicates if this connection error is temporary or fatal.
|
||||||
|
func (e *peerNotRespondingError) Temporary() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handshaker defines a ALTS handshaker interface.
|
||||||
|
type Handshaker interface {
|
||||||
|
// ClientHandshake starts and completes a client-side handshaking and
|
||||||
|
// returns a secure connection and corresponding auth information.
|
||||||
|
ClientHandshake(ctx context.Context) (net.Conn, credentials.AuthInfo, error)
|
||||||
|
// ServerHandshake starts and completes a server-side handshaking and
|
||||||
|
// returns a secure connection and corresponding auth information.
|
||||||
|
ServerHandshake(ctx context.Context) (net.Conn, credentials.AuthInfo, error)
|
||||||
|
// Close terminates the Handshaker. It should be called when the caller
|
||||||
|
// obtains the secure connection.
|
||||||
|
Close()
|
||||||
|
}
|
131
credentials/alts/core/conn/aeadrekey.go
Normal file
131
credentials/alts/core/conn/aeadrekey.go
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package conn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// rekeyAEAD holds the necessary information for an AEAD based on
|
||||||
|
// AES-GCM that performs nonce-based key derivation and XORs the
|
||||||
|
// nonce with a random mask.
|
||||||
|
type rekeyAEAD struct {
|
||||||
|
kdfKey []byte
|
||||||
|
kdfCounter []byte
|
||||||
|
nonceMask []byte
|
||||||
|
nonceBuf []byte
|
||||||
|
gcmAEAD cipher.AEAD
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeySizeError signals that the given key does not have the correct size.
|
||||||
|
type KeySizeError int
|
||||||
|
|
||||||
|
func (k KeySizeError) Error() string {
|
||||||
|
return "alts/conn: invalid key size " + strconv.Itoa(int(k))
|
||||||
|
}
|
||||||
|
|
||||||
|
// newRekeyAEAD creates a new instance of aes128gcm with rekeying.
|
||||||
|
// The key argument should be 44 bytes, the first 32 bytes are used as a key
|
||||||
|
// for HKDF-expand and the remainining 12 bytes are used as a random mask for
|
||||||
|
// the counter.
|
||||||
|
func newRekeyAEAD(key []byte) (*rekeyAEAD, error) {
|
||||||
|
k := len(key)
|
||||||
|
if k != kdfKeyLen+nonceLen {
|
||||||
|
return nil, KeySizeError(k)
|
||||||
|
}
|
||||||
|
return &rekeyAEAD{
|
||||||
|
kdfKey: key[:kdfKeyLen],
|
||||||
|
kdfCounter: make([]byte, kdfCounterLen),
|
||||||
|
nonceMask: key[kdfKeyLen:],
|
||||||
|
nonceBuf: make([]byte, nonceLen),
|
||||||
|
gcmAEAD: nil,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seal rekeys if nonce[2:8] is different than in the last call, masks the nonce,
|
||||||
|
// and calls Seal for aes128gcm.
|
||||||
|
func (s *rekeyAEAD) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||||
|
if err := s.rekeyIfRequired(nonce); err != nil {
|
||||||
|
panic(fmt.Sprintf("Rekeying failed with: %s", err.Error()))
|
||||||
|
}
|
||||||
|
maskNonce(s.nonceBuf, nonce, s.nonceMask)
|
||||||
|
return s.gcmAEAD.Seal(dst, s.nonceBuf, plaintext, additionalData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open rekeys if nonce[2:8] is different than in the last call, masks the nonce,
|
||||||
|
// and calls Open for aes128gcm.
|
||||||
|
func (s *rekeyAEAD) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||||
|
if err := s.rekeyIfRequired(nonce); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
maskNonce(s.nonceBuf, nonce, s.nonceMask)
|
||||||
|
return s.gcmAEAD.Open(dst, s.nonceBuf, ciphertext, additionalData)
|
||||||
|
}
|
||||||
|
|
||||||
|
// rekeyIfRequired creates a new aes128gcm AEAD if the existing AEAD is nil
|
||||||
|
// or cannot be used with given nonce.
|
||||||
|
func (s *rekeyAEAD) rekeyIfRequired(nonce []byte) error {
|
||||||
|
newKdfCounter := nonce[kdfCounterOffset : kdfCounterOffset+kdfCounterLen]
|
||||||
|
if s.gcmAEAD != nil && bytes.Equal(newKdfCounter, s.kdfCounter) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
copy(s.kdfCounter, newKdfCounter)
|
||||||
|
a, err := aes.NewCipher(hkdfExpand(s.kdfKey, s.kdfCounter))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s.gcmAEAD, err = cipher.NewGCM(a)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// maskNonce XORs the given nonce with the mask and stores the result in dst.
|
||||||
|
func maskNonce(dst, nonce, mask []byte) {
|
||||||
|
nonce1 := binary.LittleEndian.Uint64(nonce[:sizeUint64])
|
||||||
|
nonce2 := binary.LittleEndian.Uint32(nonce[sizeUint64:])
|
||||||
|
mask1 := binary.LittleEndian.Uint64(mask[:sizeUint64])
|
||||||
|
mask2 := binary.LittleEndian.Uint32(mask[sizeUint64:])
|
||||||
|
binary.LittleEndian.PutUint64(dst[:sizeUint64], nonce1^mask1)
|
||||||
|
binary.LittleEndian.PutUint32(dst[sizeUint64:], nonce2^mask2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NonceSize returns the required nonce size.
|
||||||
|
func (s *rekeyAEAD) NonceSize() int {
|
||||||
|
return s.gcmAEAD.NonceSize()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overhead returns the ciphertext overhead.
|
||||||
|
func (s *rekeyAEAD) Overhead() int {
|
||||||
|
return s.gcmAEAD.Overhead()
|
||||||
|
}
|
||||||
|
|
||||||
|
// hkdfExpand computes the first 16 bytes of the HKDF-expand function
|
||||||
|
// defined in RFC5869.
|
||||||
|
func hkdfExpand(key, info []byte) []byte {
|
||||||
|
mac := hmac.New(sha256.New, key)
|
||||||
|
mac.Write(info)
|
||||||
|
mac.Write([]byte{0x01}[:])
|
||||||
|
return mac.Sum(nil)[:aeadKeyLen]
|
||||||
|
}
|
263
credentials/alts/core/conn/aeadrekey_test.go
Normal file
263
credentials/alts/core/conn/aeadrekey_test.go
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package conn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// cryptoTestVector is struct for a rekey test vector
|
||||||
|
type rekeyAEADTestVector struct {
|
||||||
|
desc string
|
||||||
|
key, nonce, plaintext, aad, ciphertext []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test encrypt and decrypt using (adapted) test vectors for AES-GCM.
|
||||||
|
func TestAES128GCMRekeyEncrypt(t *testing.T) {
|
||||||
|
for _, test := range []rekeyAEADTestVector{
|
||||||
|
// NIST vectors from:
|
||||||
|
// http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-revised-spec.pdf
|
||||||
|
//
|
||||||
|
// IEEE vectors from:
|
||||||
|
// http://www.ieee802.org/1/files/public/docs2011/bn-randall-test-vectors-0511-v1.pdf
|
||||||
|
//
|
||||||
|
// Key expanded by setting
|
||||||
|
// expandedKey = (key ||
|
||||||
|
// key ^ {0x01,..,0x01} ||
|
||||||
|
// key ^ {0x02,..,0x02})[0:44].
|
||||||
|
{
|
||||||
|
desc: "Derived from NIST test vector 1",
|
||||||
|
key: dehex("0000000000000000000000000000000001010101010101010101010101010101020202020202020202020202"),
|
||||||
|
nonce: dehex("000000000000000000000000"),
|
||||||
|
aad: dehex(""),
|
||||||
|
plaintext: dehex(""),
|
||||||
|
ciphertext: dehex("85e873e002f6ebdc4060954eb8675508"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from NIST test vector 2",
|
||||||
|
key: dehex("0000000000000000000000000000000001010101010101010101010101010101020202020202020202020202"),
|
||||||
|
nonce: dehex("000000000000000000000000"),
|
||||||
|
aad: dehex(""),
|
||||||
|
plaintext: dehex("00000000000000000000000000000000"),
|
||||||
|
ciphertext: dehex("51e9a8cb23ca2512c8256afff8e72d681aca19a1148ac115e83df4888cc00d11"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from NIST test vector 3",
|
||||||
|
key: dehex("feffe9928665731c6d6a8f9467308308fffee8938764721d6c6b8e9566318209fcfdeb908467711e6f688d96"),
|
||||||
|
nonce: dehex("cafebabefacedbaddecaf888"),
|
||||||
|
aad: dehex(""),
|
||||||
|
plaintext: dehex("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255"),
|
||||||
|
ciphertext: dehex("1018ed5a1402a86516d6576d70b2ffccca261b94df88b58f53b64dfba435d18b2f6e3b7869f9353d4ac8cf09afb1663daa7b4017e6fc2c177c0c087c0df1162129952213cee1bc6e9c8495dd705e1f3d"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from NIST test vector 4",
|
||||||
|
key: dehex("feffe9928665731c6d6a8f9467308308fffee8938764721d6c6b8e9566318209fcfdeb908467711e6f688d96"),
|
||||||
|
nonce: dehex("cafebabefacedbaddecaf888"),
|
||||||
|
aad: dehex("feedfacedeadbeeffeedfacedeadbeefabaddad2"),
|
||||||
|
plaintext: dehex("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39"),
|
||||||
|
ciphertext: dehex("1018ed5a1402a86516d6576d70b2ffccca261b94df88b58f53b64dfba435d18b2f6e3b7869f9353d4ac8cf09afb1663daa7b4017e6fc2c177c0c087c4764565d077e9124001ddb27fc0848c5"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from adapted NIST test vector 4 for KDF counter boundary (flip nonce bit 15)",
|
||||||
|
key: dehex("feffe9928665731c6d6a8f9467308308fffee8938764721d6c6b8e9566318209fcfdeb908467711e6f688d96"),
|
||||||
|
nonce: dehex("ca7ebabefacedbaddecaf888"),
|
||||||
|
aad: dehex("feedfacedeadbeeffeedfacedeadbeefabaddad2"),
|
||||||
|
plaintext: dehex("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39"),
|
||||||
|
ciphertext: dehex("e650d3c0fb879327f2d03287fa93cd07342b136215adbca00c3bd5099ec41832b1d18e0423ed26bb12c6cd09debb29230a94c0cee15903656f85edb6fc509b1b28216382172ecbcc31e1e9b1"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from adapted NIST test vector 4 for KDF counter boundary (flip nonce bit 16)",
|
||||||
|
key: dehex("feffe9928665731c6d6a8f9467308308fffee8938764721d6c6b8e9566318209fcfdeb908467711e6f688d96"),
|
||||||
|
nonce: dehex("cafebbbefacedbaddecaf888"),
|
||||||
|
aad: dehex("feedfacedeadbeeffeedfacedeadbeefabaddad2"),
|
||||||
|
plaintext: dehex("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39"),
|
||||||
|
ciphertext: dehex("c0121e6c954d0767f96630c33450999791b2da2ad05c4190169ccad9ac86ff1c721e3d82f2ad22ab463bab4a0754b7dd68ca4de7ea2531b625eda01f89312b2ab957d5c7f8568dd95fcdcd1f"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from adapted NIST test vector 4 for KDF counter boundary (flip nonce bit 63)",
|
||||||
|
key: dehex("feffe9928665731c6d6a8f9467308308fffee8938764721d6c6b8e9566318209fcfdeb908467711e6f688d96"),
|
||||||
|
nonce: dehex("cafebabefacedb2ddecaf888"),
|
||||||
|
aad: dehex("feedfacedeadbeeffeedfacedeadbeefabaddad2"),
|
||||||
|
plaintext: dehex("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39"),
|
||||||
|
ciphertext: dehex("8af37ea5684a4d81d4fd817261fd9743099e7e6a025eaacf8e54b124fb5743149e05cb89f4a49467fe2e5e5965f29a19f99416b0016b54585d12553783ba59e9f782e82e097c336bf7989f08"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from adapted NIST test vector 4 for KDF counter boundary (flip nonce bit 64)",
|
||||||
|
key: dehex("feffe9928665731c6d6a8f9467308308fffee8938764721d6c6b8e9566318209fcfdeb908467711e6f688d96"),
|
||||||
|
nonce: dehex("cafebabefacedbaddfcaf888"),
|
||||||
|
aad: dehex("feedfacedeadbeeffeedfacedeadbeefabaddad2"),
|
||||||
|
plaintext: dehex("d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39"),
|
||||||
|
ciphertext: dehex("fbd528448d0346bfa878634864d407a35a039de9db2f1feb8e965b3ae9356ce6289441d77f8f0df294891f37ea438b223e3bf2bdc53d4c5a74fb680bb312a8dec6f7252cbcd7f5799750ad78"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from IEEE 2.1.1 54-byte auth",
|
||||||
|
key: dehex("ad7a2bd03eac835a6f620fdcb506b345ac7b2ad13fad825b6e630eddb407b244af7829d23cae81586d600dde"),
|
||||||
|
nonce: dehex("12153524c0895e81b2c28465"),
|
||||||
|
aad: dehex("d609b1f056637a0d46df998d88e5222ab2c2846512153524c0895e8108000f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233340001"),
|
||||||
|
plaintext: dehex(""),
|
||||||
|
ciphertext: dehex("3ea0b584f3c85e93f9320ea591699efb"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from IEEE 2.1.2 54-byte auth",
|
||||||
|
key: dehex("e3c08a8f06c6e3ad95a70557b23f75483ce33021a9c72b7025666204c69c0b72e1c2888d04c4e1af97a50755"),
|
||||||
|
nonce: dehex("12153524c0895e81b2c28465"),
|
||||||
|
aad: dehex("d609b1f056637a0d46df998d88e5222ab2c2846512153524c0895e8108000f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233340001"),
|
||||||
|
plaintext: dehex(""),
|
||||||
|
ciphertext: dehex("294e028bf1fe6f14c4e8f7305c933eb5"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from IEEE 2.2.1 60-byte crypt",
|
||||||
|
key: dehex("ad7a2bd03eac835a6f620fdcb506b345ac7b2ad13fad825b6e630eddb407b244af7829d23cae81586d600dde"),
|
||||||
|
nonce: dehex("12153524c0895e81b2c28465"),
|
||||||
|
aad: dehex("d609b1f056637a0d46df998d88e52e00b2c2846512153524c0895e81"),
|
||||||
|
plaintext: dehex("08000f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a0002"),
|
||||||
|
ciphertext: dehex("db3d25719c6b0a3ca6145c159d5c6ed9aff9c6e0b79f17019ea923b8665ddf52137ad611f0d1bf417a7ca85e45afe106ff9c7569d335d086ae6c03f00987ccd6"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from IEEE 2.2.2 60-byte crypt",
|
||||||
|
key: dehex("e3c08a8f06c6e3ad95a70557b23f75483ce33021a9c72b7025666204c69c0b72e1c2888d04c4e1af97a50755"),
|
||||||
|
nonce: dehex("12153524c0895e81b2c28465"),
|
||||||
|
aad: dehex("d609b1f056637a0d46df998d88e52e00b2c2846512153524c0895e81"),
|
||||||
|
plaintext: dehex("08000f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a0002"),
|
||||||
|
ciphertext: dehex("1641f28ec13afcc8f7903389787201051644914933e9202bb9d06aa020c2a67ef51dfe7bc00a856c55b8f8133e77f659132502bad63f5713d57d0c11e0f871ed"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from IEEE 2.3.1 60-byte auth",
|
||||||
|
key: dehex("071b113b0ca743fecccf3d051f737382061a103a0da642ffcdce3c041e727283051913390ea541fccecd3f07"),
|
||||||
|
nonce: dehex("f0761e8dcd3d000176d457ed"),
|
||||||
|
aad: dehex("e20106d7cd0df0761e8dcd3d88e5400076d457ed08000f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a0003"),
|
||||||
|
plaintext: dehex(""),
|
||||||
|
ciphertext: dehex("58837a10562b0f1f8edbe58ca55811d3"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from IEEE 2.3.2 60-byte auth",
|
||||||
|
key: dehex("691d3ee909d7f54167fd1ca0b5d769081f2bde1aee655fdbab80bd5295ae6be76b1f3ceb0bd5f74365ff1ea2"),
|
||||||
|
nonce: dehex("f0761e8dcd3d000176d457ed"),
|
||||||
|
aad: dehex("e20106d7cd0df0761e8dcd3d88e5400076d457ed08000f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a0003"),
|
||||||
|
plaintext: dehex(""),
|
||||||
|
ciphertext: dehex("c2722ff6ca29a257718a529d1f0c6a3b"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from IEEE 2.4.1 54-byte crypt",
|
||||||
|
key: dehex("071b113b0ca743fecccf3d051f737382061a103a0da642ffcdce3c041e727283051913390ea541fccecd3f07"),
|
||||||
|
nonce: dehex("f0761e8dcd3d000176d457ed"),
|
||||||
|
aad: dehex("e20106d7cd0df0761e8dcd3d88e54c2a76d457ed"),
|
||||||
|
plaintext: dehex("08000f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233340004"),
|
||||||
|
ciphertext: dehex("fd96b715b93a13346af51e8acdf792cdc7b2686f8574c70e6b0cbf16291ded427ad73fec48cd298e0528a1f4c644a949fc31dc9279706ddba33f"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from IEEE 2.4.2 54-byte crypt",
|
||||||
|
key: dehex("691d3ee909d7f54167fd1ca0b5d769081f2bde1aee655fdbab80bd5295ae6be76b1f3ceb0bd5f74365ff1ea2"),
|
||||||
|
nonce: dehex("f0761e8dcd3d000176d457ed"),
|
||||||
|
aad: dehex("e20106d7cd0df0761e8dcd3d88e54c2a76d457ed"),
|
||||||
|
plaintext: dehex("08000f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f30313233340004"),
|
||||||
|
ciphertext: dehex("b68f6300c2e9ae833bdc070e24021a3477118e78ccf84e11a485d861476c300f175353d5cdf92008a4f878e6cc3577768085c50a0e98fda6cbb8"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from IEEE 2.5.1 65-byte auth",
|
||||||
|
key: dehex("013fe00b5f11be7f866d0cbbc55a7a90003ee10a5e10bf7e876c0dbac45b7b91033de2095d13bc7d846f0eb9"),
|
||||||
|
nonce: dehex("7cfde9f9e33724c68932d612"),
|
||||||
|
aad: dehex("84c5d513d2aaf6e5bbd2727788e523008932d6127cfde9f9e33724c608000f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f0005"),
|
||||||
|
plaintext: dehex(""),
|
||||||
|
ciphertext: dehex("cca20eecda6283f09bb3543dd99edb9b"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from IEEE 2.5.2 65-byte auth",
|
||||||
|
key: dehex("83c093b58de7ffe1c0da926ac43fb3609ac1c80fee1b624497ef942e2f79a82381c291b78fe5fde3c2d89068"),
|
||||||
|
nonce: dehex("7cfde9f9e33724c68932d612"),
|
||||||
|
aad: dehex("84c5d513d2aaf6e5bbd2727788e523008932d6127cfde9f9e33724c608000f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f0005"),
|
||||||
|
plaintext: dehex(""),
|
||||||
|
ciphertext: dehex("b232cc1da5117bf15003734fa599d271"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from IEEE 2.6.1 61-byte crypt",
|
||||||
|
key: dehex("013fe00b5f11be7f866d0cbbc55a7a90003ee10a5e10bf7e876c0dbac45b7b91033de2095d13bc7d846f0eb9"),
|
||||||
|
nonce: dehex("7cfde9f9e33724c68932d612"),
|
||||||
|
aad: dehex("84c5d513d2aaf6e5bbd2727788e52f008932d6127cfde9f9e33724c6"),
|
||||||
|
plaintext: dehex("08000f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b0006"),
|
||||||
|
ciphertext: dehex("ff1910d35ad7e5657890c7c560146fd038707f204b66edbc3d161f8ace244b985921023c436e3a1c3532ecd5d09a056d70be583f0d10829d9387d07d33d872e490"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from IEEE 2.6.2 61-byte crypt",
|
||||||
|
key: dehex("83c093b58de7ffe1c0da926ac43fb3609ac1c80fee1b624497ef942e2f79a82381c291b78fe5fde3c2d89068"),
|
||||||
|
nonce: dehex("7cfde9f9e33724c68932d612"),
|
||||||
|
aad: dehex("84c5d513d2aaf6e5bbd2727788e52f008932d6127cfde9f9e33724c6"),
|
||||||
|
plaintext: dehex("08000f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b0006"),
|
||||||
|
ciphertext: dehex("0db4cf956b5f97eca4eab82a6955307f9ae02a32dd7d93f83d66ad04e1cfdc5182ad12abdea5bbb619a1bd5fb9a573590fba908e9c7a46c1f7ba0905d1b55ffda4"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from IEEE 2.7.1 79-byte crypt",
|
||||||
|
key: dehex("88ee087fd95da9fbf6725aa9d757b0cd89ef097ed85ca8faf7735ba8d656b1cc8aec0a7ddb5fabf9f47058ab"),
|
||||||
|
nonce: dehex("7ae8e2ca4ec500012e58495c"),
|
||||||
|
aad: dehex("68f2e77696ce7ae8e2ca4ec588e541002e58495c08000f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d0007"),
|
||||||
|
plaintext: dehex(""),
|
||||||
|
ciphertext: dehex("813f0e630f96fb2d030f58d83f5cdfd0"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from IEEE 2.7.2 79-byte crypt",
|
||||||
|
key: dehex("4c973dbc7364621674f8b5b89e5c15511fced9216490fb1c1a2caa0ffe0407e54e953fbe7166601476fab7ba"),
|
||||||
|
nonce: dehex("7ae8e2ca4ec500012e58495c"),
|
||||||
|
aad: dehex("68f2e77696ce7ae8e2ca4ec588e541002e58495c08000f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d0007"),
|
||||||
|
plaintext: dehex(""),
|
||||||
|
ciphertext: dehex("77e5a44c21eb07188aacbd74d1980e97"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from IEEE 2.8.1 61-byte crypt",
|
||||||
|
key: dehex("88ee087fd95da9fbf6725aa9d757b0cd89ef097ed85ca8faf7735ba8d656b1cc8aec0a7ddb5fabf9f47058ab"),
|
||||||
|
nonce: dehex("7ae8e2ca4ec500012e58495c"),
|
||||||
|
aad: dehex("68f2e77696ce7ae8e2ca4ec588e54d002e58495c"),
|
||||||
|
plaintext: dehex("08000f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748490008"),
|
||||||
|
ciphertext: dehex("958ec3f6d60afeda99efd888f175e5fcd4c87b9bcc5c2f5426253a8b506296c8c43309ab2adb5939462541d95e80811e04e706b1498f2c407c7fb234f8cc01a647550ee6b557b35a7e3945381821f4"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Derived from IEEE 2.8.2 61-byte crypt",
|
||||||
|
key: dehex("4c973dbc7364621674f8b5b89e5c15511fced9216490fb1c1a2caa0ffe0407e54e953fbe7166601476fab7ba"),
|
||||||
|
nonce: dehex("7ae8e2ca4ec500012e58495c"),
|
||||||
|
aad: dehex("68f2e77696ce7ae8e2ca4ec588e54d002e58495c"),
|
||||||
|
plaintext: dehex("08000f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748490008"),
|
||||||
|
ciphertext: dehex("b44d072011cd36d272a9b7a98db9aa90cbc5c67b93ddce67c854503214e2e896ec7e9db649ed4bcf6f850aac0223d0cf92c83db80795c3a17ecc1248bb00591712b1ae71e268164196252162810b00"),
|
||||||
|
}} {
|
||||||
|
aead, err := newRekeyAEAD(test.key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("unexpected failure in newRekeyAEAD: ", err.Error())
|
||||||
|
}
|
||||||
|
if got := aead.Seal(nil, test.nonce, test.plaintext, test.aad); !bytes.Equal(got, test.ciphertext) {
|
||||||
|
t.Errorf("Unexpected ciphertext for test vector '%s':\nciphertext=%s\nwant= %s",
|
||||||
|
test.desc, hex.EncodeToString(got), hex.EncodeToString(test.ciphertext))
|
||||||
|
}
|
||||||
|
if got, err := aead.Open(nil, test.nonce, test.ciphertext, test.aad); err != nil || !bytes.Equal(got, test.plaintext) {
|
||||||
|
t.Errorf("Unexpected plaintext for test vector '%s':\nplaintext=%s (err=%v)\nwant= %s",
|
||||||
|
test.desc, hex.EncodeToString(got), err, hex.EncodeToString(test.plaintext))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dehex(s string) []byte {
|
||||||
|
if len(s) == 0 {
|
||||||
|
return make([]byte, 0)
|
||||||
|
}
|
||||||
|
b, err := hex.DecodeString(s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
105
credentials/alts/core/conn/aes128gcm.go
Normal file
105
credentials/alts/core/conn/aes128gcm.go
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package conn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/credentials/alts/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Overflow length n in bytes, never encrypt more than 2^(n*8) frames (in
|
||||||
|
// each direction).
|
||||||
|
overflowLenAES128GCM = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
// aes128gcm is the struct that holds necessary information for ALTS record.
|
||||||
|
// The counter value is NOT included in the payload during the encryption and
|
||||||
|
// decryption operations.
|
||||||
|
type aes128gcm struct {
|
||||||
|
// inCounter is used in ALTS record to check that incoming counters are
|
||||||
|
// as expected, since ALTS record guarantees that messages are unwrapped
|
||||||
|
// in the same order that the peer wrapped them.
|
||||||
|
inCounter counter
|
||||||
|
outCounter counter
|
||||||
|
aead cipher.AEAD
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAES128GCM creates an instance that uses aes128gcm for ALTS record.
|
||||||
|
func NewAES128GCM(side core.Side, key []byte) (ALTSRecordCrypto, error) {
|
||||||
|
c, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
a, err := cipher.NewGCM(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &aes128gcm{
|
||||||
|
inCounter: newInCounter(side, overflowLenAES128GCM),
|
||||||
|
outCounter: newOutCounter(side, overflowLenAES128GCM),
|
||||||
|
aead: a,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt is the encryption function. dst can contain bytes at the beginning of
|
||||||
|
// the ciphertext that will not be encrypted but will be authenticated. If dst
|
||||||
|
// has enough capacity to hold these bytes, the ciphertext and the tag, no
|
||||||
|
// allocation and copy operations will be performed. dst and plaintext do not
|
||||||
|
// overlap.
|
||||||
|
func (s *aes128gcm) Encrypt(dst, plaintext []byte) ([]byte, error) {
|
||||||
|
// If we need to allocate an output buffer, we want to include space for
|
||||||
|
// GCM tag to avoid forcing ALTS record to reallocate as well.
|
||||||
|
dlen := len(dst)
|
||||||
|
dst, out := SliceForAppend(dst, len(plaintext)+GcmTagSize)
|
||||||
|
seq, err := s.outCounter.Value()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data := out[:len(plaintext)]
|
||||||
|
copy(data, plaintext) // data may alias plaintext
|
||||||
|
|
||||||
|
// Seal appends the ciphertext and the tag to its first argument and
|
||||||
|
// returns the updated slice. However, SliceForAppend above ensures that
|
||||||
|
// dst has enough capacity to avoid a reallocation and copy due to the
|
||||||
|
// append.
|
||||||
|
dst = s.aead.Seal(dst[:dlen], seq, data, nil)
|
||||||
|
s.outCounter.Inc()
|
||||||
|
return dst, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *aes128gcm) EncryptionOverhead() int {
|
||||||
|
return GcmTagSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *aes128gcm) Decrypt(dst, ciphertext []byte) ([]byte, error) {
|
||||||
|
seq, err := s.inCounter.Value()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// If dst is equal to ciphertext[:0], ciphertext storage is reused.
|
||||||
|
plaintext, err := s.aead.Open(dst, seq, ciphertext, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrAuth
|
||||||
|
}
|
||||||
|
s.inCounter.Inc()
|
||||||
|
return plaintext, nil
|
||||||
|
}
|
223
credentials/alts/core/conn/aes128gcm_test.go
Normal file
223
credentials/alts/core/conn/aes128gcm_test.go
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package conn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/credentials/alts/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
// cryptoTestVector is struct for a GCM test vector
|
||||||
|
type cryptoTestVector struct {
|
||||||
|
key, counter, plaintext, ciphertext, tag []byte
|
||||||
|
allocateDst bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// getGCMCryptoPair outputs a client/server pair on aes128gcm.
|
||||||
|
func getGCMCryptoPair(key []byte, counter []byte, t *testing.T) (ALTSRecordCrypto, ALTSRecordCrypto) {
|
||||||
|
client, err := NewAES128GCM(core.ClientSide, key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewAES128GCM(ClientSide, key) = %v", err)
|
||||||
|
}
|
||||||
|
server, err := NewAES128GCM(core.ServerSide, key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewAES128GCM(ServerSide, key) = %v", err)
|
||||||
|
}
|
||||||
|
// set counter if provided.
|
||||||
|
if counter != nil {
|
||||||
|
if counterSide(counter) == core.ClientSide {
|
||||||
|
client.(*aes128gcm).outCounter = counterFromValue(counter, overflowLenAES128GCM)
|
||||||
|
server.(*aes128gcm).inCounter = counterFromValue(counter, overflowLenAES128GCM)
|
||||||
|
} else {
|
||||||
|
server.(*aes128gcm).outCounter = counterFromValue(counter, overflowLenAES128GCM)
|
||||||
|
client.(*aes128gcm).inCounter = counterFromValue(counter, overflowLenAES128GCM)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return client, server
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGCMEncryptionDecryption(sender ALTSRecordCrypto, receiver ALTSRecordCrypto, test *cryptoTestVector, withCounter bool, t *testing.T) {
|
||||||
|
// Ciphertext is: counter + encrypted text + tag.
|
||||||
|
ciphertext := []byte(nil)
|
||||||
|
if withCounter {
|
||||||
|
ciphertext = append(ciphertext, test.counter...)
|
||||||
|
}
|
||||||
|
ciphertext = append(ciphertext, test.ciphertext...)
|
||||||
|
ciphertext = append(ciphertext, test.tag...)
|
||||||
|
|
||||||
|
// Decrypt.
|
||||||
|
if got, err := receiver.Decrypt(nil, ciphertext); err != nil || !bytes.Equal(got, test.plaintext) {
|
||||||
|
t.Errorf("key=%v\ncounter=%v\ntag=%v\nciphertext=%v\nDecrypt = %v, %v\nwant: %v",
|
||||||
|
test.key, test.counter, test.tag, test.ciphertext, got, err, test.plaintext)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt.
|
||||||
|
var dst []byte
|
||||||
|
if test.allocateDst {
|
||||||
|
dst = make([]byte, len(test.plaintext)+sender.EncryptionOverhead())
|
||||||
|
}
|
||||||
|
if got, err := sender.Encrypt(dst[:0], test.plaintext); err != nil || !bytes.Equal(got, ciphertext) {
|
||||||
|
t.Errorf("key=%v\ncounter=%v\nplaintext=%v\nEncrypt = %v, %v\nwant: %v",
|
||||||
|
test.key, test.counter, test.plaintext, got, err, ciphertext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test encrypt and decrypt using test vectors for aes128gcm.
|
||||||
|
func TestAES128GCMEncrypt(t *testing.T) {
|
||||||
|
for _, test := range []cryptoTestVector{
|
||||||
|
{
|
||||||
|
key: dehex("11754cd72aec309bf52f7687212e8957"),
|
||||||
|
counter: dehex("3c819d9a9bed087615030b65"),
|
||||||
|
plaintext: nil,
|
||||||
|
ciphertext: nil,
|
||||||
|
tag: dehex("250327c674aaf477aef2675748cf6971"),
|
||||||
|
allocateDst: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: dehex("ca47248ac0b6f8372a97ac43508308ed"),
|
||||||
|
counter: dehex("ffd2b598feabc9019262d2be"),
|
||||||
|
plaintext: nil,
|
||||||
|
ciphertext: nil,
|
||||||
|
tag: dehex("60d20404af527d248d893ae495707d1a"),
|
||||||
|
allocateDst: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: dehex("7fddb57453c241d03efbed3ac44e371c"),
|
||||||
|
counter: dehex("ee283a3fc75575e33efd4887"),
|
||||||
|
plaintext: dehex("d5de42b461646c255c87bd2962d3b9a2"),
|
||||||
|
ciphertext: dehex("2ccda4a5415cb91e135c2a0f78c9b2fd"),
|
||||||
|
tag: dehex("b36d1df9b9d5e596f83e8b7f52971cb3"),
|
||||||
|
allocateDst: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: dehex("ab72c77b97cb5fe9a382d9fe81ffdbed"),
|
||||||
|
counter: dehex("54cc7dc2c37ec006bcc6d1da"),
|
||||||
|
plaintext: dehex("007c5e5b3e59df24a7c355584fc1518d"),
|
||||||
|
ciphertext: dehex("0e1bde206a07a9c2c1b65300f8c64997"),
|
||||||
|
tag: dehex("2b4401346697138c7a4891ee59867d0c"),
|
||||||
|
allocateDst: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: dehex("11754cd72aec309bf52f7687212e8957"),
|
||||||
|
counter: dehex("3c819d9a9bed087615030b65"),
|
||||||
|
plaintext: nil,
|
||||||
|
ciphertext: nil,
|
||||||
|
tag: dehex("250327c674aaf477aef2675748cf6971"),
|
||||||
|
allocateDst: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: dehex("ca47248ac0b6f8372a97ac43508308ed"),
|
||||||
|
counter: dehex("ffd2b598feabc9019262d2be"),
|
||||||
|
plaintext: nil,
|
||||||
|
ciphertext: nil,
|
||||||
|
tag: dehex("60d20404af527d248d893ae495707d1a"),
|
||||||
|
allocateDst: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: dehex("7fddb57453c241d03efbed3ac44e371c"),
|
||||||
|
counter: dehex("ee283a3fc75575e33efd4887"),
|
||||||
|
plaintext: dehex("d5de42b461646c255c87bd2962d3b9a2"),
|
||||||
|
ciphertext: dehex("2ccda4a5415cb91e135c2a0f78c9b2fd"),
|
||||||
|
tag: dehex("b36d1df9b9d5e596f83e8b7f52971cb3"),
|
||||||
|
allocateDst: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: dehex("ab72c77b97cb5fe9a382d9fe81ffdbed"),
|
||||||
|
counter: dehex("54cc7dc2c37ec006bcc6d1da"),
|
||||||
|
plaintext: dehex("007c5e5b3e59df24a7c355584fc1518d"),
|
||||||
|
ciphertext: dehex("0e1bde206a07a9c2c1b65300f8c64997"),
|
||||||
|
tag: dehex("2b4401346697138c7a4891ee59867d0c"),
|
||||||
|
allocateDst: true,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
// Test encryption and decryption for aes128gcm.
|
||||||
|
client, server := getGCMCryptoPair(test.key, test.counter, t)
|
||||||
|
if counterSide(test.counter) == core.ClientSide {
|
||||||
|
testGCMEncryptionDecryption(client, server, &test, false, t)
|
||||||
|
} else {
|
||||||
|
testGCMEncryptionDecryption(server, client, &test, false, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGCMEncryptRoundtrip(client ALTSRecordCrypto, server ALTSRecordCrypto, t *testing.T) {
|
||||||
|
// Encrypt.
|
||||||
|
const plaintext = "This is plaintext."
|
||||||
|
var err error
|
||||||
|
buf := []byte(plaintext)
|
||||||
|
buf, err = client.Encrypt(buf[:0], buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Encrypting with client-side context: unexpected error", err, "\n",
|
||||||
|
"Plaintext:", []byte(plaintext))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt a second message.
|
||||||
|
const plaintext2 = "This is a second plaintext."
|
||||||
|
buf2 := []byte(plaintext2)
|
||||||
|
buf2, err = client.Encrypt(buf2[:0], buf2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Encrypting with client-side context: unexpected error", err, "\n",
|
||||||
|
"Plaintext:", []byte(plaintext2))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decryption fails: cannot decrypt second message before first.
|
||||||
|
if got, err := server.Decrypt(nil, buf2); err == nil {
|
||||||
|
t.Error("Decrypting client-side ciphertext with a client-side context unexpectedly succeeded; want unexpected counter error:\n",
|
||||||
|
" Original plaintext:", []byte(plaintext2), "\n",
|
||||||
|
" Ciphertext:", buf2, "\n",
|
||||||
|
" Decrypted plaintext:", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decryption fails: wrong counter space.
|
||||||
|
if got, err := client.Decrypt(nil, buf); err == nil {
|
||||||
|
t.Error("Decrypting client-side ciphertext with a client-side context unexpectedly succeeded; want counter space error:\n",
|
||||||
|
" Original plaintext:", []byte(plaintext), "\n",
|
||||||
|
" Ciphertext:", buf, "\n",
|
||||||
|
" Decrypted plaintext:", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt first message.
|
||||||
|
ciphertext := append([]byte(nil), buf...)
|
||||||
|
buf, err = server.Decrypt(buf[:0], buf)
|
||||||
|
if err != nil || string(buf) != plaintext {
|
||||||
|
t.Fatal("Decrypting client-side ciphertext with a server-side context did not produce original content:\n",
|
||||||
|
" Original plaintext:", []byte(plaintext), "\n",
|
||||||
|
" Ciphertext:", ciphertext, "\n",
|
||||||
|
" Decryption error:", err, "\n",
|
||||||
|
" Decrypted plaintext:", buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decryption fails: replay attack.
|
||||||
|
if got, err := server.Decrypt(nil, buf); err == nil {
|
||||||
|
t.Error("Decrypting client-side ciphertext with a client-side context unexpectedly succeeded; want unexpected counter error:\n",
|
||||||
|
" Original plaintext:", []byte(plaintext), "\n",
|
||||||
|
" Ciphertext:", buf, "\n",
|
||||||
|
" Decrypted plaintext:", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test encrypt and decrypt on roundtrip messages for aes128gcm.
|
||||||
|
func TestAES128GCMEncryptRoundtrip(t *testing.T) {
|
||||||
|
// Test for aes128gcm.
|
||||||
|
key := make([]byte, 16)
|
||||||
|
client, server := getGCMCryptoPair(key, nil, t)
|
||||||
|
testGCMEncryptRoundtrip(client, server, t)
|
||||||
|
}
|
116
credentials/alts/core/conn/aes128gcmrekey.go
Normal file
116
credentials/alts/core/conn/aes128gcmrekey.go
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package conn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/cipher"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/credentials/alts/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Overflow length n in bytes, never encrypt more than 2^(n*8) frames (in
|
||||||
|
// each direction).
|
||||||
|
overflowLenAES128GCMRekey = 8
|
||||||
|
nonceLen = 12
|
||||||
|
aeadKeyLen = 16
|
||||||
|
kdfKeyLen = 32
|
||||||
|
kdfCounterOffset = 2
|
||||||
|
kdfCounterLen = 6
|
||||||
|
sizeUint64 = 8
|
||||||
|
)
|
||||||
|
|
||||||
|
// aes128gcmRekey is the struct that holds necessary information for ALTS record.
|
||||||
|
// The counter value is NOT included in the payload during the encryption and
|
||||||
|
// decryption operations.
|
||||||
|
type aes128gcmRekey struct {
|
||||||
|
// inCounter is used in ALTS record to check that incoming counters are
|
||||||
|
// as expected, since ALTS record guarantees that messages are unwrapped
|
||||||
|
// in the same order that the peer wrapped them.
|
||||||
|
inCounter counter
|
||||||
|
outCounter counter
|
||||||
|
inAEAD cipher.AEAD
|
||||||
|
outAEAD cipher.AEAD
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewAES128GCMRekey creates an instance that uses aes128gcm with rekeying
|
||||||
|
// for ALTS record. The key argument should be 44 bytes, the first 32 bytes
|
||||||
|
// are used as a key for HKDF-expand and the remainining 12 bytes are used
|
||||||
|
// as a random mask for the counter.
|
||||||
|
func NewAES128GCMRekey(side core.Side, key []byte) (ALTSRecordCrypto, error) {
|
||||||
|
inCounter := newInCounter(side, overflowLenAES128GCMRekey)
|
||||||
|
outCounter := newOutCounter(side, overflowLenAES128GCMRekey)
|
||||||
|
inAEAD, err := newRekeyAEAD(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
outAEAD, err := newRekeyAEAD(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &aes128gcmRekey{
|
||||||
|
inCounter,
|
||||||
|
outCounter,
|
||||||
|
inAEAD,
|
||||||
|
outAEAD,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt is the encryption function. dst can contain bytes at the beginning of
|
||||||
|
// the ciphertext that will not be encrypted but will be authenticated. If dst
|
||||||
|
// has enough capacity to hold these bytes, the ciphertext and the tag, no
|
||||||
|
// allocation and copy operations will be performed. dst and plaintext do not
|
||||||
|
// overlap.
|
||||||
|
func (s *aes128gcmRekey) Encrypt(dst, plaintext []byte) ([]byte, error) {
|
||||||
|
// If we need to allocate an output buffer, we want to include space for
|
||||||
|
// GCM tag to avoid forcing ALTS record to reallocate as well.
|
||||||
|
dlen := len(dst)
|
||||||
|
dst, out := SliceForAppend(dst, len(plaintext)+GcmTagSize)
|
||||||
|
seq, err := s.outCounter.Value()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data := out[:len(plaintext)]
|
||||||
|
copy(data, plaintext) // data may alias plaintext
|
||||||
|
|
||||||
|
// Seal appends the ciphertext and the tag to its first argument and
|
||||||
|
// returns the updated slice. However, SliceForAppend above ensures that
|
||||||
|
// dst has enough capacity to avoid a reallocation and copy due to the
|
||||||
|
// append.
|
||||||
|
dst = s.outAEAD.Seal(dst[:dlen], seq, data, nil)
|
||||||
|
s.outCounter.Inc()
|
||||||
|
return dst, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *aes128gcmRekey) EncryptionOverhead() int {
|
||||||
|
return GcmTagSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *aes128gcmRekey) Decrypt(dst, ciphertext []byte) ([]byte, error) {
|
||||||
|
seq, err := s.inCounter.Value()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
plaintext, err := s.inAEAD.Open(dst, seq, ciphertext, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, ErrAuth
|
||||||
|
}
|
||||||
|
s.inCounter.Inc()
|
||||||
|
return plaintext, nil
|
||||||
|
}
|
117
credentials/alts/core/conn/aes128gcmrekey_test.go
Normal file
117
credentials/alts/core/conn/aes128gcmrekey_test.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package conn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/credentials/alts/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
// cryptoTestVector is struct for a rekey test vector
|
||||||
|
type rekeyTestVector struct {
|
||||||
|
key, nonce, plaintext, ciphertext []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// getGCMCryptoPair outputs a client/server pair on aes128gcmRekey.
|
||||||
|
func getRekeyCryptoPair(key []byte, counter []byte, t *testing.T) (ALTSRecordCrypto, ALTSRecordCrypto) {
|
||||||
|
client, err := NewAES128GCMRekey(core.ClientSide, key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewAES128GCMRekey(ClientSide, key) = %v", err)
|
||||||
|
}
|
||||||
|
server, err := NewAES128GCMRekey(core.ServerSide, key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewAES128GCMRekey(ServerSide, key) = %v", err)
|
||||||
|
}
|
||||||
|
// set counter if provided.
|
||||||
|
if counter != nil {
|
||||||
|
if counterSide(counter) == core.ClientSide {
|
||||||
|
client.(*aes128gcmRekey).outCounter = counterFromValue(counter, overflowLenAES128GCMRekey)
|
||||||
|
server.(*aes128gcmRekey).inCounter = counterFromValue(counter, overflowLenAES128GCMRekey)
|
||||||
|
} else {
|
||||||
|
server.(*aes128gcmRekey).outCounter = counterFromValue(counter, overflowLenAES128GCMRekey)
|
||||||
|
client.(*aes128gcmRekey).inCounter = counterFromValue(counter, overflowLenAES128GCMRekey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return client, server
|
||||||
|
}
|
||||||
|
|
||||||
|
func testRekeyEncryptRoundtrip(client ALTSRecordCrypto, server ALTSRecordCrypto, t *testing.T) {
|
||||||
|
// Encrypt.
|
||||||
|
const plaintext = "This is plaintext."
|
||||||
|
var err error
|
||||||
|
buf := []byte(plaintext)
|
||||||
|
buf, err = client.Encrypt(buf[:0], buf)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Encrypting with client-side context: unexpected error", err, "\n",
|
||||||
|
"Plaintext:", []byte(plaintext))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encrypt a second message.
|
||||||
|
const plaintext2 = "This is a second plaintext."
|
||||||
|
buf2 := []byte(plaintext2)
|
||||||
|
buf2, err = client.Encrypt(buf2[:0], buf2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Encrypting with client-side context: unexpected error", err, "\n",
|
||||||
|
"Plaintext:", []byte(plaintext2))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decryption fails: cannot decrypt second message before first.
|
||||||
|
if got, err := server.Decrypt(nil, buf2); err == nil {
|
||||||
|
t.Error("Decrypting client-side ciphertext with a client-side context unexpectedly succeeded; want unexpected counter error:\n",
|
||||||
|
" Original plaintext:", []byte(plaintext2), "\n",
|
||||||
|
" Ciphertext:", buf2, "\n",
|
||||||
|
" Decrypted plaintext:", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decryption fails: wrong counter space.
|
||||||
|
if got, err := client.Decrypt(nil, buf); err == nil {
|
||||||
|
t.Error("Decrypting client-side ciphertext with a client-side context unexpectedly succeeded; want counter space error:\n",
|
||||||
|
" Original plaintext:", []byte(plaintext), "\n",
|
||||||
|
" Ciphertext:", buf, "\n",
|
||||||
|
" Decrypted plaintext:", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decrypt first message.
|
||||||
|
ciphertext := append([]byte(nil), buf...)
|
||||||
|
buf, err = server.Decrypt(buf[:0], buf)
|
||||||
|
if err != nil || string(buf) != plaintext {
|
||||||
|
t.Fatal("Decrypting client-side ciphertext with a server-side context did not produce original content:\n",
|
||||||
|
" Original plaintext:", []byte(plaintext), "\n",
|
||||||
|
" Ciphertext:", ciphertext, "\n",
|
||||||
|
" Decryption error:", err, "\n",
|
||||||
|
" Decrypted plaintext:", buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decryption fails: replay attack.
|
||||||
|
if got, err := server.Decrypt(nil, buf); err == nil {
|
||||||
|
t.Error("Decrypting client-side ciphertext with a client-side context unexpectedly succeeded; want unexpected counter error:\n",
|
||||||
|
" Original plaintext:", []byte(plaintext), "\n",
|
||||||
|
" Ciphertext:", buf, "\n",
|
||||||
|
" Decrypted plaintext:", got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test encrypt and decrypt on roundtrip messages for aes128gcmRekey.
|
||||||
|
func TestAES128GCMRekeyEncryptRoundtrip(t *testing.T) {
|
||||||
|
// Test for aes128gcmRekey.
|
||||||
|
key := make([]byte, 44)
|
||||||
|
client, server := getRekeyCryptoPair(key, nil, t)
|
||||||
|
testRekeyEncryptRoundtrip(client, server, t)
|
||||||
|
}
|
70
credentials/alts/core/conn/common.go
Normal file
70
credentials/alts/core/conn/common.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package conn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// GcmTagSize is the GCM tag size is the difference in length between
|
||||||
|
// plaintext and ciphertext. From crypto/cipher/gcm.go in Go crypto
|
||||||
|
// library.
|
||||||
|
GcmTagSize = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrAuth occurs on authentication failure.
|
||||||
|
var ErrAuth = errors.New("message authentication failed")
|
||||||
|
|
||||||
|
// SliceForAppend takes a slice and a requested number of bytes. It returns a
|
||||||
|
// slice with the contents of the given slice followed by that many bytes and a
|
||||||
|
// second slice that aliases into it and contains only the extra bytes. If the
|
||||||
|
// original slice has sufficient capacity then no allocation is performed.
|
||||||
|
func SliceForAppend(in []byte, n int) (head, tail []byte) {
|
||||||
|
if total := len(in) + n; cap(in) >= total {
|
||||||
|
head = in[:total]
|
||||||
|
} else {
|
||||||
|
head = make([]byte, total)
|
||||||
|
copy(head, in)
|
||||||
|
}
|
||||||
|
tail = head[len(in):]
|
||||||
|
return head, tail
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseFramedMsg parse the provided buffer and returns a frame of the format
|
||||||
|
// msgLength+msg and any remaining bytes in that buffer.
|
||||||
|
func ParseFramedMsg(b []byte, maxLen uint32) ([]byte, []byte, error) {
|
||||||
|
// If the size field is not complete, return the provided buffer as
|
||||||
|
// remaining buffer.
|
||||||
|
if len(b) < MsgLenFieldSize {
|
||||||
|
return nil, b, nil
|
||||||
|
}
|
||||||
|
msgLenField := b[:MsgLenFieldSize]
|
||||||
|
length := binary.LittleEndian.Uint32(msgLenField)
|
||||||
|
if length > maxLen {
|
||||||
|
return nil, nil, fmt.Errorf("received the frame length %d larger than the limit %d", length, maxLen)
|
||||||
|
}
|
||||||
|
if len(b) < int(length)+4 { // account for the first 4 msg length bytes.
|
||||||
|
// Frame is not complete yet.
|
||||||
|
return nil, b, nil
|
||||||
|
}
|
||||||
|
return b[:MsgLenFieldSize+length], b[MsgLenFieldSize+length:], nil
|
||||||
|
}
|
106
credentials/alts/core/conn/counter.go
Normal file
106
credentials/alts/core/conn/counter.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package conn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/credentials/alts/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
const counterLen = 12
|
||||||
|
|
||||||
|
var (
|
||||||
|
errInvalidCounter = errors.New("invalid counter")
|
||||||
|
)
|
||||||
|
|
||||||
|
// counter is a 96-bit, little-endian counter.
|
||||||
|
type counter struct {
|
||||||
|
value [counterLen]byte
|
||||||
|
invalid bool
|
||||||
|
overflowLen int
|
||||||
|
}
|
||||||
|
|
||||||
|
// newOutCounter returns an outgoing counter initialized to the starting sequence
|
||||||
|
// number for the client/server side of a connection.
|
||||||
|
func newOutCounter(s core.Side, overflowLen int) (c counter) {
|
||||||
|
c.overflowLen = overflowLen
|
||||||
|
if s == core.ServerSide {
|
||||||
|
// Server counters in ALTS record have the little-endian high bit
|
||||||
|
// set.
|
||||||
|
c.value[counterLen-1] = 0x80
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// newInCounter returns an incoming counter initialized to the starting sequence
|
||||||
|
// number for the client/server side of a connection. This is used in ALTS record
|
||||||
|
// to check that incoming counters are as expected, since ALTS record guarantees
|
||||||
|
// that messages are unwrapped in the same order that the peer wrapped them.
|
||||||
|
func newInCounter(s core.Side, overflowLen int) (c counter) {
|
||||||
|
c.overflowLen = overflowLen
|
||||||
|
if s == core.ClientSide {
|
||||||
|
// Server counters in ALTS record have the little-endian high bit
|
||||||
|
// set.
|
||||||
|
c.value[counterLen-1] = 0x80
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// counterFromValue creates a new counter given an initial value.
|
||||||
|
func counterFromValue(value []byte, overflowLen int) (c counter) {
|
||||||
|
c.overflowLen = overflowLen
|
||||||
|
copy(c.value[:], value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the current value of the counter as a byte slice.
|
||||||
|
func (c *counter) Value() ([]byte, error) {
|
||||||
|
if c.invalid {
|
||||||
|
return nil, errInvalidCounter
|
||||||
|
}
|
||||||
|
return c.value[:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inc increments the counter and checks for overflow.
|
||||||
|
func (c *counter) Inc() {
|
||||||
|
// If the counter is already invalid, there is not need to increase it.
|
||||||
|
if c.invalid {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
i := 0
|
||||||
|
for ; i < c.overflowLen; i++ {
|
||||||
|
c.value[i]++
|
||||||
|
if c.value[i] != 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i == c.overflowLen {
|
||||||
|
c.invalid = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// counterSide returns the connection side (client/server) a sequence counter is
|
||||||
|
// associated with.
|
||||||
|
func counterSide(c []byte) core.Side {
|
||||||
|
if c[counterLen-1]&0x80 == 0x80 {
|
||||||
|
return core.ServerSide
|
||||||
|
}
|
||||||
|
return core.ClientSide
|
||||||
|
}
|
141
credentials/alts/core/conn/counter_test.go
Normal file
141
credentials/alts/core/conn/counter_test.go
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package conn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/credentials/alts/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
testOverflowLen = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCounterSides(t *testing.T) {
|
||||||
|
for _, side := range []core.Side{core.ClientSide, core.ServerSide} {
|
||||||
|
outCounter := newOutCounter(side, testOverflowLen)
|
||||||
|
inCounter := newInCounter(side, testOverflowLen)
|
||||||
|
for i := 0; i < 1024; i++ {
|
||||||
|
value, _ := outCounter.Value()
|
||||||
|
if g, w := counterSide(value), side; g != w {
|
||||||
|
t.Errorf("after %d iterations, counterSide(outCounter.Value()) = %v, want %v", i, g, w)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
value, _ = inCounter.Value()
|
||||||
|
if g, w := counterSide(value), side; g == w {
|
||||||
|
t.Errorf("after %d iterations, counterSide(inCounter.Value()) = %v, want %v", i, g, w)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
outCounter.Inc()
|
||||||
|
inCounter.Inc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCounterInc(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
counter []byte
|
||||||
|
want []byte
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
counter: []byte{0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
want: []byte{0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
counter: []byte{0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80},
|
||||||
|
want: []byte{0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
counter: []byte{0xff, 0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
want: []byte{0x00, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
counter: []byte{0x42, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
want: []byte{0x43, 0xff, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
counter: []byte{0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
want: []byte{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
counter: []byte{0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80},
|
||||||
|
want: []byte{0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80},
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
c := counterFromValue(test.counter, overflowLenAES128GCM)
|
||||||
|
c.Inc()
|
||||||
|
value, _ := c.Value()
|
||||||
|
if g, w := value, test.want; !bytes.Equal(g, w) || c.invalid {
|
||||||
|
t.Errorf("counter(%v).Inc() =\n%v, want\n%v", test.counter, g, w)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRolloverCounter(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
desc string
|
||||||
|
value []byte
|
||||||
|
overflowLen int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "testing overflow without rekeying 1",
|
||||||
|
value: []byte{0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80},
|
||||||
|
overflowLen: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "testing overflow without rekeying 2",
|
||||||
|
value: []byte{0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
overflowLen: 5,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "testing overflow for rekeying mode 1",
|
||||||
|
value: []byte{0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x80},
|
||||||
|
overflowLen: 8,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "testing overflow for rekeying mode 2",
|
||||||
|
value: []byte{0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00},
|
||||||
|
overflowLen: 8,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
c := counterFromValue(test.value, overflowLenAES128GCM)
|
||||||
|
|
||||||
|
// First Inc() + Value() should work.
|
||||||
|
c.Inc()
|
||||||
|
_, err := c.Value()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%v: first Inc() + Value() unexpectedly failed: %v, want <nil> error", test.desc, err)
|
||||||
|
}
|
||||||
|
// Second Inc() + Value() should fail.
|
||||||
|
c.Inc()
|
||||||
|
_, err = c.Value()
|
||||||
|
if err != errInvalidCounter {
|
||||||
|
t.Errorf("%v: second Inc() + Value() unexpectedly succeeded: want %v", test.desc, errInvalidCounter)
|
||||||
|
}
|
||||||
|
// Third Inc() + Value() should also fail because the counter is
|
||||||
|
// already in an invalid state.
|
||||||
|
c.Inc()
|
||||||
|
_, err = c.Value()
|
||||||
|
if err != errInvalidCounter {
|
||||||
|
t.Errorf("%v: Third Inc() + Value() unexpectedly succeeded: want %v", test.desc, errInvalidCounter)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
271
credentials/alts/core/conn/record.go
Normal file
271
credentials/alts/core/conn/record.go
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package conn contains an implementation of a secure channel created by gRPC
|
||||||
|
// handshakers.
|
||||||
|
package conn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/credentials/alts/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ALTSRecordCrypto is the interface for gRPC ALTS record protocol.
|
||||||
|
type ALTSRecordCrypto interface {
|
||||||
|
// Encrypt encrypts the plaintext and computes the tag (if any) of dst
|
||||||
|
// and plaintext, dst and plaintext do not overlap.
|
||||||
|
Encrypt(dst, plaintext []byte) ([]byte, error)
|
||||||
|
// EncryptionOverhead returns the tag size (if any) in bytes.
|
||||||
|
EncryptionOverhead() int
|
||||||
|
// Decrypt decrypts ciphertext and verify the tag (if any). dst and
|
||||||
|
// ciphertext may alias exactly or not at all. To reuse ciphertext's
|
||||||
|
// storage for the decrypted output, use ciphertext[:0] as dst.
|
||||||
|
Decrypt(dst, ciphertext []byte) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ALTSRecordFunc is a function type for factory functions that create
|
||||||
|
// ALTSRecordCrypto instances.
|
||||||
|
type ALTSRecordFunc func(s core.Side, keyData []byte) (ALTSRecordCrypto, error)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// MsgLenFieldSize is the byte size of the frame length field of a
|
||||||
|
// framed message.
|
||||||
|
MsgLenFieldSize = 4
|
||||||
|
// The byte size of the message type field of a framed message.
|
||||||
|
msgTypeFieldSize = 4
|
||||||
|
// The bytes size limit for a ALTS record message.
|
||||||
|
altsRecordLengthLimit = 1024 * 1024 // 1 MiB
|
||||||
|
// The default bytes size of a ALTS record message.
|
||||||
|
altsRecordDefaultLength = 4 * 1024 // 4KiB
|
||||||
|
// Message type value included in ALTS record framing.
|
||||||
|
altsRecordMsgType = uint32(0x06)
|
||||||
|
// The initial write buffer size.
|
||||||
|
altsWriteBufferInitialSize = 32 * 1024 // 32KiB
|
||||||
|
// The maximum write buffer size. This *must* be multiple of
|
||||||
|
// altsRecordDefaultLength.
|
||||||
|
altsWriteBufferMaxSize = 512 * 1024 // 512KiB
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
protocols = make(map[string]ALTSRecordFunc)
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterProtocol register a ALTS record encryption protocol.
|
||||||
|
func RegisterProtocol(protocol string, f ALTSRecordFunc) error {
|
||||||
|
if _, ok := protocols[protocol]; ok {
|
||||||
|
return fmt.Errorf("protocol %v is already registered", protocol)
|
||||||
|
}
|
||||||
|
protocols[protocol] = f
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// conn represents a secured connection. It implements the net.Conn interface.
|
||||||
|
type conn struct {
|
||||||
|
net.Conn
|
||||||
|
crypto ALTSRecordCrypto
|
||||||
|
// buf holds data that has been read from the connection and decrypted,
|
||||||
|
// but has not yet been returned by Read.
|
||||||
|
buf []byte
|
||||||
|
payloadLengthLimit int
|
||||||
|
// protected holds data read from the network but have not yet been
|
||||||
|
// decrypted. This data might not compose a complete frame.
|
||||||
|
protected []byte
|
||||||
|
// writeBuf is a buffer used to contain encrypted frames before being
|
||||||
|
// written to the network.
|
||||||
|
writeBuf []byte
|
||||||
|
// nextFrame stores the next frame (in protected buffer) info.
|
||||||
|
nextFrame []byte
|
||||||
|
// overhead is the calculated overhead of each frame.
|
||||||
|
overhead int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConn creates a new secure channel instance given the other party role and
|
||||||
|
// handshaking result.
|
||||||
|
func NewConn(c net.Conn, side core.Side, recordProtocol string, key []byte, protected []byte) (net.Conn, error) {
|
||||||
|
newCrypto := protocols[recordProtocol]
|
||||||
|
if newCrypto == nil {
|
||||||
|
return nil, fmt.Errorf("negotiated unknown next_protocol %q", recordProtocol)
|
||||||
|
}
|
||||||
|
crypto, err := newCrypto(side, key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("protocol %q: %v", recordProtocol, err)
|
||||||
|
}
|
||||||
|
overhead := MsgLenFieldSize + msgTypeFieldSize + crypto.EncryptionOverhead()
|
||||||
|
payloadLengthLimit := altsRecordDefaultLength - overhead
|
||||||
|
if protected == nil {
|
||||||
|
// We pre-allocate protected to be of size
|
||||||
|
// 2*altsRecordDefaultLength-1 during initialization. We only
|
||||||
|
// read from the network into protected when protected does not
|
||||||
|
// contain a complete frame, which is at most
|
||||||
|
// altsRecordDefaultLength-1 (bytes). And we read at most
|
||||||
|
// altsRecordDefaultLength (bytes) data into protected at one
|
||||||
|
// time. Therefore, 2*altsRecordDefaultLength-1 is large enough
|
||||||
|
// to buffer data read from the network.
|
||||||
|
protected = make([]byte, 0, 2*altsRecordDefaultLength-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
altsConn := &conn{
|
||||||
|
Conn: c,
|
||||||
|
crypto: crypto,
|
||||||
|
payloadLengthLimit: payloadLengthLimit,
|
||||||
|
protected: protected,
|
||||||
|
writeBuf: make([]byte, altsWriteBufferInitialSize),
|
||||||
|
nextFrame: protected,
|
||||||
|
overhead: overhead,
|
||||||
|
}
|
||||||
|
return altsConn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads and decrypts a frame from the underlying connection, and copies the
|
||||||
|
// decrypted payload into b. If the size of the payload is greater than len(b),
|
||||||
|
// Read retains the remaining bytes in an internal buffer, and subsequent calls
|
||||||
|
// to Read will read from this buffer until it is exhausted.
|
||||||
|
func (p *conn) Read(b []byte) (n int, err error) {
|
||||||
|
if len(p.buf) == 0 {
|
||||||
|
var framedMsg []byte
|
||||||
|
framedMsg, p.nextFrame, err = ParseFramedMsg(p.nextFrame, altsRecordLengthLimit)
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
// Check whether the next frame to be decrypted has been
|
||||||
|
// completely received yet.
|
||||||
|
if len(framedMsg) == 0 {
|
||||||
|
copy(p.protected, p.nextFrame)
|
||||||
|
p.protected = p.protected[:len(p.nextFrame)]
|
||||||
|
// Always copy next incomplete frame to the beginning of
|
||||||
|
// the protected buffer and reset nextFrame to it.
|
||||||
|
p.nextFrame = p.protected
|
||||||
|
}
|
||||||
|
// Check whether a complete frame has been received yet.
|
||||||
|
for len(framedMsg) == 0 {
|
||||||
|
if len(p.protected) == cap(p.protected) {
|
||||||
|
tmp := make([]byte, len(p.protected), cap(p.protected)+altsRecordDefaultLength)
|
||||||
|
copy(tmp, p.protected)
|
||||||
|
p.protected = tmp
|
||||||
|
}
|
||||||
|
n, err = p.Conn.Read(p.protected[len(p.protected):min(cap(p.protected), len(p.protected)+altsRecordDefaultLength)])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
p.protected = p.protected[:len(p.protected)+n]
|
||||||
|
framedMsg, p.nextFrame, err = ParseFramedMsg(p.protected, altsRecordLengthLimit)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Now we have a complete frame, decrypted it.
|
||||||
|
msg := framedMsg[MsgLenFieldSize:]
|
||||||
|
msgType := binary.LittleEndian.Uint32(msg[:msgTypeFieldSize])
|
||||||
|
if msgType&0xff != altsRecordMsgType {
|
||||||
|
return 0, fmt.Errorf("received frame with incorrect message type %v, expected lower byte %v",
|
||||||
|
msgType, altsRecordMsgType)
|
||||||
|
}
|
||||||
|
ciphertext := msg[msgTypeFieldSize:]
|
||||||
|
|
||||||
|
// Decrypt requires that if the dst and ciphertext alias, they
|
||||||
|
// must alias exactly. Code here used to use msg[:0], but msg
|
||||||
|
// starts MsgLenFieldSize+msgTypeFieldSize bytes earlier than
|
||||||
|
// ciphertext, so they alias inexactly. Using ciphertext[:0]
|
||||||
|
// arranges the appropriate aliasing without needing to copy
|
||||||
|
// ciphertext or use a separate destination buffer. For more info
|
||||||
|
// check: https://golang.org/pkg/crypto/cipher/#AEAD.
|
||||||
|
p.buf, err = p.crypto.Decrypt(ciphertext[:0], ciphertext)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n = copy(b, p.buf)
|
||||||
|
p.buf = p.buf[n:]
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write encrypts, frames, and writes bytes from b to the underlying connection.
|
||||||
|
func (p *conn) Write(b []byte) (n int, err error) {
|
||||||
|
n = len(b)
|
||||||
|
// Calculate the output buffer size with framing and encryption overhead.
|
||||||
|
numOfFrames := int(math.Ceil(float64(len(b)) / float64(p.payloadLengthLimit)))
|
||||||
|
size := len(b) + numOfFrames*p.overhead
|
||||||
|
// If writeBuf is too small, increase its size up to the maximum size.
|
||||||
|
partialBSize := len(b)
|
||||||
|
if size > altsWriteBufferMaxSize {
|
||||||
|
size = altsWriteBufferMaxSize
|
||||||
|
const numOfFramesInMaxWriteBuf = altsWriteBufferMaxSize / altsRecordDefaultLength
|
||||||
|
partialBSize = numOfFramesInMaxWriteBuf * p.payloadLengthLimit
|
||||||
|
}
|
||||||
|
if len(p.writeBuf) < size {
|
||||||
|
p.writeBuf = make([]byte, size)
|
||||||
|
}
|
||||||
|
|
||||||
|
for partialBStart := 0; partialBStart < len(b); partialBStart += partialBSize {
|
||||||
|
partialBEnd := partialBStart + partialBSize
|
||||||
|
if partialBEnd > len(b) {
|
||||||
|
partialBEnd = len(b)
|
||||||
|
}
|
||||||
|
partialB := b[partialBStart:partialBEnd]
|
||||||
|
writeBufIndex := 0
|
||||||
|
for len(partialB) > 0 {
|
||||||
|
payloadLen := len(partialB)
|
||||||
|
if payloadLen > p.payloadLengthLimit {
|
||||||
|
payloadLen = p.payloadLengthLimit
|
||||||
|
}
|
||||||
|
buf := partialB[:payloadLen]
|
||||||
|
partialB = partialB[payloadLen:]
|
||||||
|
|
||||||
|
// Write buffer contains: length, type, payload, and tag
|
||||||
|
// if any.
|
||||||
|
|
||||||
|
// 1. Fill in type field.
|
||||||
|
msg := p.writeBuf[writeBufIndex+MsgLenFieldSize:]
|
||||||
|
binary.LittleEndian.PutUint32(msg, altsRecordMsgType)
|
||||||
|
|
||||||
|
// 2. Encrypt the payload and create a tag if any.
|
||||||
|
msg, err = p.crypto.Encrypt(msg[:msgTypeFieldSize], buf)
|
||||||
|
if err != nil {
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Fill in the size field.
|
||||||
|
binary.LittleEndian.PutUint32(p.writeBuf[writeBufIndex:], uint32(len(msg)))
|
||||||
|
|
||||||
|
// 4. Increase writeBufIndex.
|
||||||
|
writeBufIndex += len(buf) + p.overhead
|
||||||
|
}
|
||||||
|
nn, err := p.Conn.Write(p.writeBuf[:writeBufIndex])
|
||||||
|
if err != nil {
|
||||||
|
// We need to calculate the actual data size that was
|
||||||
|
// written. This means we need to remove header,
|
||||||
|
// encryption overheads, and any partially-written
|
||||||
|
// frame data.
|
||||||
|
numOfWrittenFrames := int(math.Floor(float64(nn) / float64(altsRecordDefaultLength)))
|
||||||
|
return partialBStart + numOfWrittenFrames*p.payloadLengthLimit, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func min(a, b int) int {
|
||||||
|
if a < b {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
274
credentials/alts/core/conn/record_test.go
Normal file
274
credentials/alts/core/conn/record_test.go
Normal file
@ -0,0 +1,274 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package conn
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"net"
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/credentials/alts/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
nextProtocols = []string{"ALTSRP_GCM_AES128"}
|
||||||
|
altsRecordFuncs = map[string]ALTSRecordFunc{
|
||||||
|
// ALTS handshaker protocols.
|
||||||
|
"ALTSRP_GCM_AES128": func(s core.Side, keyData []byte) (ALTSRecordCrypto, error) {
|
||||||
|
return NewAES128GCM(s, keyData)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for protocol, f := range altsRecordFuncs {
|
||||||
|
if err := RegisterProtocol(protocol, f); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testConn mimics a net.Conn to the peer.
|
||||||
|
type testConn struct {
|
||||||
|
net.Conn
|
||||||
|
in *bytes.Buffer
|
||||||
|
out *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testConn) Read(b []byte) (n int, err error) {
|
||||||
|
return c.in.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testConn) Write(b []byte) (n int, err error) {
|
||||||
|
return c.out.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *testConn) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestALTSRecordConn(in, out *bytes.Buffer, side core.Side, np string) *conn {
|
||||||
|
key := []byte{
|
||||||
|
// 16 arbitrary bytes.
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xd2, 0x4c, 0xce, 0x4f, 0x49}
|
||||||
|
tc := testConn{
|
||||||
|
in: in,
|
||||||
|
out: out,
|
||||||
|
}
|
||||||
|
c, err := NewConn(&tc, side, np, key, nil)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Unexpected error creating test ALTS record connection: %v", err))
|
||||||
|
}
|
||||||
|
return c.(*conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newConnPair(np string) (client, server *conn) {
|
||||||
|
clientBuf := new(bytes.Buffer)
|
||||||
|
serverBuf := new(bytes.Buffer)
|
||||||
|
clientConn := newTestALTSRecordConn(clientBuf, serverBuf, core.ClientSide, np)
|
||||||
|
serverConn := newTestALTSRecordConn(serverBuf, clientBuf, core.ServerSide, np)
|
||||||
|
return clientConn, serverConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPingPong(t *testing.T, np string) {
|
||||||
|
clientConn, serverConn := newConnPair(np)
|
||||||
|
clientMsg := []byte("Client Message")
|
||||||
|
if n, err := clientConn.Write(clientMsg); n != len(clientMsg) || err != nil {
|
||||||
|
t.Fatalf("Client Write() = %v, %v; want %v, <nil>", n, err, len(clientMsg))
|
||||||
|
}
|
||||||
|
rcvClientMsg := make([]byte, len(clientMsg))
|
||||||
|
if n, err := serverConn.Read(rcvClientMsg); n != len(rcvClientMsg) || err != nil {
|
||||||
|
t.Fatalf("Server Read() = %v, %v; want %v, <nil>", n, err, len(rcvClientMsg))
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(clientMsg, rcvClientMsg) {
|
||||||
|
t.Fatalf("Client Write()/Server Read() = %v, want %v", rcvClientMsg, clientMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
serverMsg := []byte("Server Message")
|
||||||
|
if n, err := serverConn.Write(serverMsg); n != len(serverMsg) || err != nil {
|
||||||
|
t.Fatalf("Server Write() = %v, %v; want %v, <nil>", n, err, len(serverMsg))
|
||||||
|
}
|
||||||
|
rcvServerMsg := make([]byte, len(serverMsg))
|
||||||
|
if n, err := clientConn.Read(rcvServerMsg); n != len(rcvServerMsg) || err != nil {
|
||||||
|
t.Fatalf("Client Read() = %v, %v; want %v, <nil>", n, err, len(rcvServerMsg))
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(serverMsg, rcvServerMsg) {
|
||||||
|
t.Fatalf("Server Write()/Client Read() = %v, want %v", rcvServerMsg, serverMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPingPong(t *testing.T) {
|
||||||
|
for _, np := range nextProtocols {
|
||||||
|
testPingPong(t, np)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSmallReadBuffer(t *testing.T, np string) {
|
||||||
|
clientConn, serverConn := newConnPair(np)
|
||||||
|
msg := []byte("Very Important Message")
|
||||||
|
if n, err := clientConn.Write(msg); err != nil {
|
||||||
|
t.Fatalf("Write() = %v, %v; want %v, <nil>", n, err, len(msg))
|
||||||
|
}
|
||||||
|
rcvMsg := make([]byte, len(msg))
|
||||||
|
n := 2 // Arbitrary index to break rcvMsg in two.
|
||||||
|
rcvMsg1 := rcvMsg[:n]
|
||||||
|
rcvMsg2 := rcvMsg[n:]
|
||||||
|
if n, err := serverConn.Read(rcvMsg1); n != len(rcvMsg1) || err != nil {
|
||||||
|
t.Fatalf("Read() = %v, %v; want %v, <nil>", n, err, len(rcvMsg1))
|
||||||
|
}
|
||||||
|
if n, err := serverConn.Read(rcvMsg2); n != len(rcvMsg2) || err != nil {
|
||||||
|
t.Fatalf("Read() = %v, %v; want %v, <nil>", n, err, len(rcvMsg2))
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(msg, rcvMsg) {
|
||||||
|
t.Fatalf("Write()/Read() = %v, want %v", rcvMsg, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSmallReadBuffer(t *testing.T) {
|
||||||
|
for _, np := range nextProtocols {
|
||||||
|
testSmallReadBuffer(t, np)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLargeMsg(t *testing.T, np string) {
|
||||||
|
clientConn, serverConn := newConnPair(np)
|
||||||
|
// msgLen is such that the length in the framing is larger than the
|
||||||
|
// default size of one frame.
|
||||||
|
msgLen := altsRecordDefaultLength - msgTypeFieldSize - clientConn.crypto.EncryptionOverhead() + 1
|
||||||
|
msg := make([]byte, msgLen)
|
||||||
|
if n, err := clientConn.Write(msg); n != len(msg) || err != nil {
|
||||||
|
t.Fatalf("Write() = %v, %v; want %v, <nil>", n, err, len(msg))
|
||||||
|
}
|
||||||
|
rcvMsg := make([]byte, len(msg))
|
||||||
|
if n, err := io.ReadFull(serverConn, rcvMsg); n != len(rcvMsg) || err != nil {
|
||||||
|
t.Fatalf("Read() = %v, %v; want %v, <nil>", n, err, len(rcvMsg))
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(msg, rcvMsg) {
|
||||||
|
t.Fatalf("Write()/Server Read() = %v, want %v", rcvMsg, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLargeMsg(t *testing.T) {
|
||||||
|
for _, np := range nextProtocols {
|
||||||
|
testLargeMsg(t, np)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testIncorrectMsgType(t *testing.T, np string) {
|
||||||
|
// framedMsg is an empty ciphertext with correct framing but wrong
|
||||||
|
// message type.
|
||||||
|
framedMsg := make([]byte, MsgLenFieldSize+msgTypeFieldSize)
|
||||||
|
binary.LittleEndian.PutUint32(framedMsg[:MsgLenFieldSize], msgTypeFieldSize)
|
||||||
|
wrongMsgType := uint32(0x22)
|
||||||
|
binary.LittleEndian.PutUint32(framedMsg[MsgLenFieldSize:], wrongMsgType)
|
||||||
|
|
||||||
|
in := bytes.NewBuffer(framedMsg)
|
||||||
|
c := newTestALTSRecordConn(in, nil, core.ClientSide, np)
|
||||||
|
b := make([]byte, 1)
|
||||||
|
if n, err := c.Read(b); n != 0 || err == nil {
|
||||||
|
t.Fatalf("Read() = <nil>, want %v", fmt.Errorf("received frame with incorrect message type %v", wrongMsgType))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIncorrectMsgType(t *testing.T) {
|
||||||
|
for _, np := range nextProtocols {
|
||||||
|
testIncorrectMsgType(t, np)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testFrameTooLarge(t *testing.T, np string) {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
clientConn := newTestALTSRecordConn(nil, buf, core.ClientSide, np)
|
||||||
|
serverConn := newTestALTSRecordConn(buf, nil, core.ServerSide, np)
|
||||||
|
// payloadLen is such that the length in the framing is larger than
|
||||||
|
// allowed in one frame.
|
||||||
|
payloadLen := altsRecordLengthLimit - msgTypeFieldSize - clientConn.crypto.EncryptionOverhead() + 1
|
||||||
|
payload := make([]byte, payloadLen)
|
||||||
|
c, err := clientConn.crypto.Encrypt(nil, payload)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf(fmt.Sprintf("Error encrypting message: %v", err))
|
||||||
|
}
|
||||||
|
msgLen := msgTypeFieldSize + len(c)
|
||||||
|
framedMsg := make([]byte, MsgLenFieldSize+msgLen)
|
||||||
|
binary.LittleEndian.PutUint32(framedMsg[:MsgLenFieldSize], uint32(msgTypeFieldSize+len(c)))
|
||||||
|
msg := framedMsg[MsgLenFieldSize:]
|
||||||
|
binary.LittleEndian.PutUint32(msg[:msgTypeFieldSize], altsRecordMsgType)
|
||||||
|
copy(msg[msgTypeFieldSize:], c)
|
||||||
|
if _, err = buf.Write(framedMsg); err != nil {
|
||||||
|
t.Fatal(fmt.Sprintf("Unexpected error writing to buffer: %v", err))
|
||||||
|
}
|
||||||
|
b := make([]byte, 1)
|
||||||
|
if n, err := serverConn.Read(b); n != 0 || err == nil {
|
||||||
|
t.Fatalf("Read() = <nil>, want %v", fmt.Errorf("received the frame length %d larger than the limit %d", altsRecordLengthLimit+1, altsRecordLengthLimit))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFrameTooLarge(t *testing.T) {
|
||||||
|
for _, np := range nextProtocols {
|
||||||
|
testFrameTooLarge(t, np)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testWriteLargeData(t *testing.T, np string) {
|
||||||
|
// Test sending and receiving messages larger than the maximum write
|
||||||
|
// buffer size.
|
||||||
|
clientConn, serverConn := newConnPair(np)
|
||||||
|
// Message size is intentionally chosen to not be multiple of
|
||||||
|
// payloadLengthLimtit.
|
||||||
|
msgSize := altsWriteBufferMaxSize + (100 * 1024)
|
||||||
|
clientMsg := make([]byte, msgSize)
|
||||||
|
for i := 0; i < msgSize; i++ {
|
||||||
|
clientMsg[i] = 0xAA
|
||||||
|
}
|
||||||
|
if n, err := clientConn.Write(clientMsg); n != len(clientMsg) || err != nil {
|
||||||
|
t.Fatalf("Client Write() = %v, %v; want %v, <nil>", n, err, len(clientMsg))
|
||||||
|
}
|
||||||
|
// We need to keep reading until the entire message is received. The
|
||||||
|
// reason we set all bytes of the message to a value other than zero is
|
||||||
|
// to avoid ambiguous zero-init value of rcvClientMsg buffer and the
|
||||||
|
// actual received data.
|
||||||
|
rcvClientMsg := make([]byte, 0, msgSize)
|
||||||
|
numberOfExpectedFrames := int(math.Ceil(float64(msgSize) / float64(serverConn.payloadLengthLimit)))
|
||||||
|
for i := 0; i < numberOfExpectedFrames; i++ {
|
||||||
|
expectedRcvSize := serverConn.payloadLengthLimit
|
||||||
|
if i == numberOfExpectedFrames-1 {
|
||||||
|
// Last frame might be smaller.
|
||||||
|
expectedRcvSize = msgSize % serverConn.payloadLengthLimit
|
||||||
|
}
|
||||||
|
tmpBuf := make([]byte, expectedRcvSize)
|
||||||
|
if n, err := serverConn.Read(tmpBuf); n != len(tmpBuf) || err != nil {
|
||||||
|
t.Fatalf("Server Read() = %v, %v; want %v, <nil>", n, err, len(tmpBuf))
|
||||||
|
}
|
||||||
|
rcvClientMsg = append(rcvClientMsg, tmpBuf...)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(clientMsg, rcvClientMsg) {
|
||||||
|
t.Fatalf("Client Write()/Server Read() = %v, want %v", rcvClientMsg, clientMsg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteLargeData(t *testing.T) {
|
||||||
|
for _, np := range nextProtocols {
|
||||||
|
testWriteLargeData(t, np)
|
||||||
|
}
|
||||||
|
}
|
364
credentials/alts/core/handshaker/handshaker.go
Normal file
364
credentials/alts/core/handshaker/handshaker.go
Normal file
@ -0,0 +1,364 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package handshaker provides ALTS handshaking functionality for GCP.
|
||||||
|
package handshaker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
|
"google.golang.org/grpc/credentials/alts/core"
|
||||||
|
"google.golang.org/grpc/credentials/alts/core/authinfo"
|
||||||
|
"google.golang.org/grpc/credentials/alts/core/conn"
|
||||||
|
altspb "google.golang.org/grpc/credentials/alts/core/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// The maximum byte size of receive frames.
|
||||||
|
frameLimit = 64 * 1024 // 64 KB
|
||||||
|
rekeyRecordProtocolName = "ALTSRP_GCM_AES128_REKEY"
|
||||||
|
// maxPendingHandshakes represents the maximum number of concurrent
|
||||||
|
// handshakes.
|
||||||
|
maxPendingHandshakes = 100
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
hsProtocol = altspb.HandshakeProtocol_ALTS
|
||||||
|
appProtocols = []string{"grpc"}
|
||||||
|
recordProtocols = []string{rekeyRecordProtocolName}
|
||||||
|
keyLength = map[string]int{
|
||||||
|
rekeyRecordProtocolName: 44,
|
||||||
|
}
|
||||||
|
altsRecordFuncs = map[string]conn.ALTSRecordFunc{
|
||||||
|
// ALTS handshaker protocols.
|
||||||
|
rekeyRecordProtocolName: func(s core.Side, keyData []byte) (conn.ALTSRecordCrypto, error) {
|
||||||
|
return conn.NewAES128GCMRekey(s, keyData)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// control number of concurrent created (but not closed) handshakers.
|
||||||
|
mu sync.Mutex
|
||||||
|
concurrentHandshakes = int64(0)
|
||||||
|
// errDropped occurs when maxPendingHandshakes is reached.
|
||||||
|
errDropped = errors.New("maximum number of concurrent ALTS handshakes is reached")
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
for protocol, f := range altsRecordFuncs {
|
||||||
|
if err := conn.RegisterProtocol(protocol, f); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func acquire(n int64) bool {
|
||||||
|
mu.Lock()
|
||||||
|
success := maxPendingHandshakes-concurrentHandshakes >= n
|
||||||
|
if success {
|
||||||
|
concurrentHandshakes += n
|
||||||
|
}
|
||||||
|
mu.Unlock()
|
||||||
|
return success
|
||||||
|
}
|
||||||
|
|
||||||
|
func release(n int64) {
|
||||||
|
mu.Lock()
|
||||||
|
concurrentHandshakes -= n
|
||||||
|
if concurrentHandshakes < 0 {
|
||||||
|
mu.Unlock()
|
||||||
|
panic("bad release")
|
||||||
|
}
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientHandshakerOptions contains the client handshaker options that can
|
||||||
|
// provided by the caller.
|
||||||
|
type ClientHandshakerOptions struct {
|
||||||
|
// ClientIdentity is the handshaker client local identity.
|
||||||
|
ClientIdentity *altspb.Identity
|
||||||
|
// TargetName is the server service account name for secure name
|
||||||
|
// checking.
|
||||||
|
TargetName string
|
||||||
|
// TargetServiceAccounts contains a list of expected target service
|
||||||
|
// accounts. One of these accounts should match one of the accounts in
|
||||||
|
// the handshaker results. Otherwise, the handshake fails.
|
||||||
|
TargetServiceAccounts []string
|
||||||
|
// RPCVersions specifies the gRPC versions accepted by the client.
|
||||||
|
RPCVersions *altspb.RpcProtocolVersions
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandshakerOptions contains the server handshaker options that can
|
||||||
|
// provided by the caller.
|
||||||
|
type ServerHandshakerOptions struct {
|
||||||
|
// RPCVersions specifies the gRPC versions accepted by the server.
|
||||||
|
RPCVersions *altspb.RpcProtocolVersions
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultClientHandshakerOptions returns the default client handshaker options.
|
||||||
|
func DefaultClientHandshakerOptions() *ClientHandshakerOptions {
|
||||||
|
return &ClientHandshakerOptions{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultServerHandshakerOptions returns the default client handshaker options.
|
||||||
|
func DefaultServerHandshakerOptions() *ServerHandshakerOptions {
|
||||||
|
return &ServerHandshakerOptions{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: add support for future local and remote endpoint in both client options
|
||||||
|
// and server options (server options struct does not exist now. When
|
||||||
|
// caller can provide endpoints, it should be created.
|
||||||
|
|
||||||
|
// altsHandshaker is used to complete a ALTS handshaking between client and
|
||||||
|
// server. This handshaker talks to the ALTS handshaker service in the metadata
|
||||||
|
// server.
|
||||||
|
type altsHandshaker struct {
|
||||||
|
// RPC stream used to access the ALTS Handshaker service.
|
||||||
|
stream altspb.HandshakerService_DoHandshakeClient
|
||||||
|
// the connection to the peer.
|
||||||
|
conn net.Conn
|
||||||
|
// client handshake options.
|
||||||
|
clientOpts *ClientHandshakerOptions
|
||||||
|
// server handshake options.
|
||||||
|
serverOpts *ServerHandshakerOptions
|
||||||
|
// defines the side doing the handshake, client or server.
|
||||||
|
side core.Side
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewClientHandshaker creates a ALTS handshaker for GCP which contains an RPC
|
||||||
|
// stub created using the passed conn and used to talk to the ALTS Handshaker
|
||||||
|
// service in the metadata server.
|
||||||
|
func NewClientHandshaker(ctx context.Context, conn *grpc.ClientConn, c net.Conn, opts *ClientHandshakerOptions) (core.Handshaker, error) {
|
||||||
|
stream, err := altspb.NewHandshakerServiceClient(conn).DoHandshake(ctx, grpc.FailFast(false))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &altsHandshaker{
|
||||||
|
stream: stream,
|
||||||
|
conn: c,
|
||||||
|
clientOpts: opts,
|
||||||
|
side: core.ClientSide,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewServerHandshaker creates a ALTS handshaker for GCP which contains an RPC
|
||||||
|
// stub created using the passed conn and used to talk to the ALTS Handshaker
|
||||||
|
// service in the metadata server.
|
||||||
|
func NewServerHandshaker(ctx context.Context, conn *grpc.ClientConn, c net.Conn, opts *ServerHandshakerOptions) (core.Handshaker, error) {
|
||||||
|
stream, err := altspb.NewHandshakerServiceClient(conn).DoHandshake(ctx, grpc.FailFast(false))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &altsHandshaker{
|
||||||
|
stream: stream,
|
||||||
|
conn: c,
|
||||||
|
serverOpts: opts,
|
||||||
|
side: core.ServerSide,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientHandshake starts and completes a client ALTS handshaking for GCP. Once
|
||||||
|
// done, ClientHandshake returns a secure connection.
|
||||||
|
func (h *altsHandshaker) ClientHandshake(ctx context.Context) (net.Conn, credentials.AuthInfo, error) {
|
||||||
|
if !acquire(1) {
|
||||||
|
return nil, nil, errDropped
|
||||||
|
}
|
||||||
|
defer release(1)
|
||||||
|
|
||||||
|
if h.side != core.ClientSide {
|
||||||
|
return nil, nil, errors.New("only handshakers created using NewClientHandshaker can perform a client handshaker")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create target identities from service account list.
|
||||||
|
targetIdentities := make([]*altspb.Identity, 0, len(h.clientOpts.TargetServiceAccounts))
|
||||||
|
for _, account := range h.clientOpts.TargetServiceAccounts {
|
||||||
|
targetIdentities = append(targetIdentities, &altspb.Identity{
|
||||||
|
IdentityOneof: &altspb.Identity_ServiceAccount{
|
||||||
|
ServiceAccount: account,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
req := &altspb.HandshakerReq{
|
||||||
|
ReqOneof: &altspb.HandshakerReq_ClientStart{
|
||||||
|
ClientStart: &altspb.StartClientHandshakeReq{
|
||||||
|
HandshakeSecurityProtocol: hsProtocol,
|
||||||
|
ApplicationProtocols: appProtocols,
|
||||||
|
RecordProtocols: recordProtocols,
|
||||||
|
TargetIdentities: targetIdentities,
|
||||||
|
LocalIdentity: h.clientOpts.ClientIdentity,
|
||||||
|
TargetName: h.clientOpts.TargetName,
|
||||||
|
RpcVersions: h.clientOpts.RPCVersions,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, result, err := h.doHandshake(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
authInfo := authinfo.New(result)
|
||||||
|
return conn, authInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerHandshake starts and completes a server ALTS handshaking for GCP. Once
|
||||||
|
// done, ServerHandshake returns a secure connection.
|
||||||
|
func (h *altsHandshaker) ServerHandshake(ctx context.Context) (net.Conn, credentials.AuthInfo, error) {
|
||||||
|
if !acquire(1) {
|
||||||
|
return nil, nil, errDropped
|
||||||
|
}
|
||||||
|
defer release(1)
|
||||||
|
|
||||||
|
if h.side != core.ServerSide {
|
||||||
|
return nil, nil, errors.New("only handshakers created using NewServerHandshaker can perform a server handshaker")
|
||||||
|
}
|
||||||
|
|
||||||
|
p := make([]byte, frameLimit)
|
||||||
|
n, err := h.conn.Read(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare server parameters.
|
||||||
|
// TODO: currently only ALTS parameters are provided. Might need to use
|
||||||
|
// more options in the future.
|
||||||
|
params := make(map[int32]*altspb.ServerHandshakeParameters)
|
||||||
|
params[int32(altspb.HandshakeProtocol_ALTS)] = &altspb.ServerHandshakeParameters{
|
||||||
|
RecordProtocols: recordProtocols,
|
||||||
|
}
|
||||||
|
req := &altspb.HandshakerReq{
|
||||||
|
ReqOneof: &altspb.HandshakerReq_ServerStart{
|
||||||
|
ServerStart: &altspb.StartServerHandshakeReq{
|
||||||
|
ApplicationProtocols: appProtocols,
|
||||||
|
HandshakeParameters: params,
|
||||||
|
InBytes: p[:n],
|
||||||
|
RpcVersions: h.serverOpts.RPCVersions,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, result, err := h.doHandshake(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
authInfo := authinfo.New(result)
|
||||||
|
return conn, authInfo, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *altsHandshaker) doHandshake(req *altspb.HandshakerReq) (net.Conn, *altspb.HandshakerResult, error) {
|
||||||
|
resp, err := h.accessHandshakerService(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
// Check of the returned status is an error.
|
||||||
|
if resp.GetStatus() != nil {
|
||||||
|
if got, want := resp.GetStatus().Code, uint32(codes.OK); got != want {
|
||||||
|
return nil, nil, fmt.Errorf("%v", resp.GetStatus().Details)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var extra []byte
|
||||||
|
if req.GetServerStart() != nil {
|
||||||
|
extra = req.GetServerStart().GetInBytes()[resp.GetBytesConsumed():]
|
||||||
|
}
|
||||||
|
result, extra, err := h.processUntilDone(resp, extra)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
// The handshaker returns a 128 bytes key. It should be truncated based
|
||||||
|
// on the returned record protocol.
|
||||||
|
keyLen, ok := keyLength[result.RecordProtocol]
|
||||||
|
if !ok {
|
||||||
|
return nil, nil, fmt.Errorf("unknown resulted record protocol %v", result.RecordProtocol)
|
||||||
|
}
|
||||||
|
sc, err := conn.NewConn(h.conn, h.side, result.GetRecordProtocol(), result.KeyData[:keyLen], extra)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return sc, result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *altsHandshaker) accessHandshakerService(req *altspb.HandshakerReq) (*altspb.HandshakerResp, error) {
|
||||||
|
if err := h.stream.Send(req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp, err := h.stream.Recv()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// processUntilDone processes the handshake until the handshaker service returns
|
||||||
|
// the results. Handshaker service takes care of frame parsing, so we read
|
||||||
|
// whatever received from the network and send it to the handshaker service.
|
||||||
|
func (h *altsHandshaker) processUntilDone(resp *altspb.HandshakerResp, extra []byte) (*altspb.HandshakerResult, []byte, error) {
|
||||||
|
for {
|
||||||
|
if len(resp.OutFrames) > 0 {
|
||||||
|
if _, err := h.conn.Write(resp.OutFrames); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if resp.Result != nil {
|
||||||
|
return resp.Result, extra, nil
|
||||||
|
}
|
||||||
|
buf := make([]byte, frameLimit)
|
||||||
|
n, err := h.conn.Read(buf)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
// If there is nothing to send to the handshaker service, and
|
||||||
|
// nothing is received from the peer, then we are stuck.
|
||||||
|
// This covers the case when the peer is not responding. Note
|
||||||
|
// that handshaker service connection issues are caught in
|
||||||
|
// accessHandshakerService before we even get here.
|
||||||
|
if len(resp.OutFrames) == 0 && n == 0 {
|
||||||
|
return nil, nil, core.PeerNotRespondingError
|
||||||
|
}
|
||||||
|
// Append extra bytes from the previous interaction with the
|
||||||
|
// handshaker service with the current buffer read from conn.
|
||||||
|
p := append(extra, buf[:n]...)
|
||||||
|
resp, err = h.accessHandshakerService(&altspb.HandshakerReq{
|
||||||
|
ReqOneof: &altspb.HandshakerReq_Next{
|
||||||
|
Next: &altspb.NextHandshakeMessageReq{
|
||||||
|
InBytes: p,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
// Set extra based on handshaker service response.
|
||||||
|
if n == 0 {
|
||||||
|
extra = nil
|
||||||
|
} else {
|
||||||
|
extra = buf[resp.GetBytesConsumed():n]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close terminates the Handshaker. It should be called when the caller obtains
|
||||||
|
// the secure connection.
|
||||||
|
func (h *altsHandshaker) Close() {
|
||||||
|
h.stream.CloseSend()
|
||||||
|
}
|
261
credentials/alts/core/handshaker/handshaker_test.go
Normal file
261
credentials/alts/core/handshaker/handshaker_test.go
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package handshaker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials/alts/core"
|
||||||
|
altspb "google.golang.org/grpc/credentials/alts/core/proto"
|
||||||
|
"google.golang.org/grpc/credentials/alts/core/testutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
testAppProtocols = []string{"grpc"}
|
||||||
|
testRecordProtocol = rekeyRecordProtocolName
|
||||||
|
testKey = []byte{
|
||||||
|
// 44 arbitrary bytes.
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xd2, 0x4c, 0xce, 0x4f, 0x49,
|
||||||
|
0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xd2, 0x4c, 0xce, 0x4f, 0x49, 0x1f, 0x8b,
|
||||||
|
0xd2, 0x4c, 0xce, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2,
|
||||||
|
}
|
||||||
|
testServiceAccount = "test_service_account"
|
||||||
|
testTargetServiceAccounts = []string{testServiceAccount}
|
||||||
|
testClientIdentity = &altspb.Identity{
|
||||||
|
IdentityOneof: &altspb.Identity_Hostname{
|
||||||
|
Hostname: "i_am_a_client",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// testRPCStream mimics a altspb.HandshakerService_DoHandshakeClient object.
|
||||||
|
type testRPCStream struct {
|
||||||
|
grpc.ClientStream
|
||||||
|
t *testing.T
|
||||||
|
isClient bool
|
||||||
|
// The resp expected to be returned by Recv(). Make sure this is set to
|
||||||
|
// the content the test requires before Recv() is invoked.
|
||||||
|
recvBuf *altspb.HandshakerResp
|
||||||
|
// false if it is the first access to Handshaker service on Envelope.
|
||||||
|
first bool
|
||||||
|
// useful for testing concurrent calls.
|
||||||
|
delay time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testRPCStream) Recv() (*altspb.HandshakerResp, error) {
|
||||||
|
resp := t.recvBuf
|
||||||
|
t.recvBuf = nil
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testRPCStream) Send(req *altspb.HandshakerReq) error {
|
||||||
|
var resp *altspb.HandshakerResp
|
||||||
|
if !t.first {
|
||||||
|
// Generate the bytes to be returned by Recv() for the initial
|
||||||
|
// handshaking.
|
||||||
|
t.first = true
|
||||||
|
if t.isClient {
|
||||||
|
resp = &altspb.HandshakerResp{
|
||||||
|
OutFrames: testutil.MakeFrame("ClientInit"),
|
||||||
|
// Simulate consuming ServerInit.
|
||||||
|
BytesConsumed: 14,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resp = &altspb.HandshakerResp{
|
||||||
|
OutFrames: testutil.MakeFrame("ServerInit"),
|
||||||
|
// Simulate consuming ClientInit.
|
||||||
|
BytesConsumed: 14,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Add delay to test concurrent calls.
|
||||||
|
close := stat.Update()
|
||||||
|
defer close()
|
||||||
|
time.Sleep(t.delay)
|
||||||
|
|
||||||
|
// Generate the response to be returned by Recv() for the
|
||||||
|
// follow-up handshaking.
|
||||||
|
result := &altspb.HandshakerResult{
|
||||||
|
RecordProtocol: testRecordProtocol,
|
||||||
|
KeyData: testKey,
|
||||||
|
}
|
||||||
|
resp = &altspb.HandshakerResp{
|
||||||
|
Result: result,
|
||||||
|
// Simulate consuming ClientFinished or ServerFinished.
|
||||||
|
BytesConsumed: 18,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t.recvBuf = resp
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testRPCStream) CloseSend() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var stat testutil.Stats
|
||||||
|
|
||||||
|
func TestClientHandshake(t *testing.T) {
|
||||||
|
for _, testCase := range []struct {
|
||||||
|
delay time.Duration
|
||||||
|
numberOfHandshakes int
|
||||||
|
}{
|
||||||
|
{0 * time.Millisecond, 1},
|
||||||
|
{100 * time.Millisecond, 10 * maxPendingHandshakes},
|
||||||
|
} {
|
||||||
|
errc := make(chan error)
|
||||||
|
stat.Reset()
|
||||||
|
for i := 0; i < testCase.numberOfHandshakes; i++ {
|
||||||
|
stream := &testRPCStream{
|
||||||
|
t: t,
|
||||||
|
isClient: true,
|
||||||
|
}
|
||||||
|
// Preload the inbound frames.
|
||||||
|
f1 := testutil.MakeFrame("ServerInit")
|
||||||
|
f2 := testutil.MakeFrame("ServerFinished")
|
||||||
|
in := bytes.NewBuffer(f1)
|
||||||
|
in.Write(f2)
|
||||||
|
out := new(bytes.Buffer)
|
||||||
|
tc := testutil.NewTestConn(in, out)
|
||||||
|
chs := &altsHandshaker{
|
||||||
|
stream: stream,
|
||||||
|
conn: tc,
|
||||||
|
clientOpts: &ClientHandshakerOptions{
|
||||||
|
TargetServiceAccounts: testTargetServiceAccounts,
|
||||||
|
ClientIdentity: testClientIdentity,
|
||||||
|
},
|
||||||
|
side: core.ClientSide,
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
_, context, err := chs.ClientHandshake(context.Background())
|
||||||
|
if err == nil && context == nil {
|
||||||
|
panic("expected non-nil ALTS context")
|
||||||
|
}
|
||||||
|
errc <- err
|
||||||
|
chs.Close()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure all errors are expected.
|
||||||
|
for i := 0; i < testCase.numberOfHandshakes; i++ {
|
||||||
|
if err := <-errc; err != nil && err != errDropped {
|
||||||
|
t.Errorf("ClientHandshake() = _, %v, want _, <nil> or %v", err, errDropped)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that there are no concurrent calls more than the limit.
|
||||||
|
if stat.MaxConcurrentCalls > maxPendingHandshakes {
|
||||||
|
t.Errorf("Observed %d concurrent handshakes; want <= %d", stat.MaxConcurrentCalls, maxPendingHandshakes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestServerHandshake(t *testing.T) {
|
||||||
|
for _, testCase := range []struct {
|
||||||
|
delay time.Duration
|
||||||
|
numberOfHandshakes int
|
||||||
|
}{
|
||||||
|
{0 * time.Millisecond, 1},
|
||||||
|
{100 * time.Millisecond, 10 * maxPendingHandshakes},
|
||||||
|
} {
|
||||||
|
errc := make(chan error)
|
||||||
|
stat.Reset()
|
||||||
|
for i := 0; i < testCase.numberOfHandshakes; i++ {
|
||||||
|
stream := &testRPCStream{
|
||||||
|
t: t,
|
||||||
|
isClient: false,
|
||||||
|
}
|
||||||
|
// Preload the inbound frames.
|
||||||
|
f1 := testutil.MakeFrame("ClientInit")
|
||||||
|
f2 := testutil.MakeFrame("ClientFinished")
|
||||||
|
in := bytes.NewBuffer(f1)
|
||||||
|
in.Write(f2)
|
||||||
|
out := new(bytes.Buffer)
|
||||||
|
tc := testutil.NewTestConn(in, out)
|
||||||
|
shs := &altsHandshaker{
|
||||||
|
stream: stream,
|
||||||
|
conn: tc,
|
||||||
|
serverOpts: DefaultServerHandshakerOptions(),
|
||||||
|
side: core.ServerSide,
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
_, context, err := shs.ServerHandshake(context.Background())
|
||||||
|
if err == nil && context == nil {
|
||||||
|
panic("expected non-nil ALTS context")
|
||||||
|
}
|
||||||
|
errc <- err
|
||||||
|
shs.Close()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure all errors are expected.
|
||||||
|
for i := 0; i < testCase.numberOfHandshakes; i++ {
|
||||||
|
if err := <-errc; err != nil && err != errDropped {
|
||||||
|
t.Errorf("ServerHandshake() = _, %v, want _, <nil> or %v", err, errDropped)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that there are no concurrent calls more than the limit.
|
||||||
|
if stat.MaxConcurrentCalls > maxPendingHandshakes {
|
||||||
|
t.Errorf("Observed %d concurrent handshakes; want <= %d", stat.MaxConcurrentCalls, maxPendingHandshakes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testUnresponsiveRPCStream is used for testing the PeerNotResponding case.
|
||||||
|
type testUnresponsiveRPCStream struct {
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testUnresponsiveRPCStream) Recv() (*altspb.HandshakerResp, error) {
|
||||||
|
return &altspb.HandshakerResp{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testUnresponsiveRPCStream) Send(req *altspb.HandshakerReq) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *testUnresponsiveRPCStream) CloseSend() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPeerNotResponding(t *testing.T) {
|
||||||
|
stream := &testUnresponsiveRPCStream{}
|
||||||
|
chs := &altsHandshaker{
|
||||||
|
stream: stream,
|
||||||
|
conn: testutil.NewUnresponsiveTestConn(),
|
||||||
|
clientOpts: &ClientHandshakerOptions{
|
||||||
|
TargetServiceAccounts: testTargetServiceAccounts,
|
||||||
|
ClientIdentity: testClientIdentity,
|
||||||
|
},
|
||||||
|
side: core.ClientSide,
|
||||||
|
}
|
||||||
|
_, context, err := chs.ClientHandshake(context.Background())
|
||||||
|
chs.Close()
|
||||||
|
if context != nil {
|
||||||
|
t.Error("expected non-nil ALTS context")
|
||||||
|
}
|
||||||
|
if got, want := err, core.PeerNotRespondingError; got != want {
|
||||||
|
t.Errorf("ClientHandshake() = %v, want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
60
credentials/alts/core/handshaker/service/service.go
Normal file
60
credentials/alts/core/handshaker/service/service.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package service manages connections between the VM application and the ALTS
|
||||||
|
// handshaker service.
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// hsServiceAddr specifies the default ALTS handshaker service address in
|
||||||
|
// the hypervisor.
|
||||||
|
hsServiceAddr = flag.String("handshaker_service_address", "metadata.google.internal:8080", "ALTS handshaker gRPC service address")
|
||||||
|
// hsConn represents a connection to hypervisor handshaker service.
|
||||||
|
hsConn *grpc.ClientConn
|
||||||
|
mu sync.Mutex
|
||||||
|
// hsDialer will be reassigned in tests.
|
||||||
|
hsDialer = grpc.Dial
|
||||||
|
)
|
||||||
|
|
||||||
|
type dialer func(target string, opts ...grpc.DialOption) (*grpc.ClientConn, error)
|
||||||
|
|
||||||
|
// Dial dials the handshake service in the hypervisor. If a connection has
|
||||||
|
// already been established, this function returns it. Otherwise, a new
|
||||||
|
// connection is created,
|
||||||
|
func Dial() (*grpc.ClientConn, error) {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
if hsConn == nil {
|
||||||
|
// Create a new connection to the handshaker service. Note that
|
||||||
|
// this connection stays open until the application is closed.
|
||||||
|
var err error
|
||||||
|
hsConn, err = hsDialer(*hsServiceAddr, grpc.WithInsecure())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hsConn, nil
|
||||||
|
}
|
64
credentials/alts/core/handshaker/service/service_test.go
Normal file
64
credentials/alts/core/handshaker/service/service_test.go
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDial(t *testing.T) {
|
||||||
|
defer func() func() {
|
||||||
|
temp := hsDialer
|
||||||
|
hsDialer = func(target string, opts ...grpc.DialOption) (*grpc.ClientConn, error) {
|
||||||
|
return &grpc.ClientConn{}, nil
|
||||||
|
}
|
||||||
|
return func() {
|
||||||
|
hsDialer = temp
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Ensure that hsConn is nil at first.
|
||||||
|
hsConn = nil
|
||||||
|
|
||||||
|
// First call to Dial, it should create set hsConn.
|
||||||
|
conn1, err := Dial()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("first call to Dial failed: %v", err)
|
||||||
|
}
|
||||||
|
if conn1 == nil {
|
||||||
|
t.Fatal("first call to Dial()=(nil, _), want not nil")
|
||||||
|
}
|
||||||
|
if got, want := hsConn, conn1; got != want {
|
||||||
|
t.Fatalf("hsConn=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Second call to Dial() should return conn1 above.
|
||||||
|
conn2, err := Dial()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("second call to Dial() failed: %v", err)
|
||||||
|
}
|
||||||
|
if got, want := conn2, conn1; got != want {
|
||||||
|
t.Fatalf("second call to Dial()=(%v, _), want (%v,. _)", got, want)
|
||||||
|
}
|
||||||
|
if got, want := hsConn, conn1; got != want {
|
||||||
|
t.Fatalf("hsConn=%v, want %v", got, want)
|
||||||
|
}
|
||||||
|
}
|
131
credentials/alts/core/proto/altscontext.pb.go
Normal file
131
credentials/alts/core/proto/altscontext.pb.go
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// source: altscontext.proto
|
||||||
|
|
||||||
|
/*
|
||||||
|
Package grpc_gcp is a generated protocol buffer package.
|
||||||
|
|
||||||
|
It is generated from these files:
|
||||||
|
altscontext.proto
|
||||||
|
handshaker.proto
|
||||||
|
transport_security_common.proto
|
||||||
|
|
||||||
|
It has these top-level messages:
|
||||||
|
AltsContext
|
||||||
|
Endpoint
|
||||||
|
Identity
|
||||||
|
StartClientHandshakeReq
|
||||||
|
ServerHandshakeParameters
|
||||||
|
StartServerHandshakeReq
|
||||||
|
NextHandshakeMessageReq
|
||||||
|
HandshakerReq
|
||||||
|
HandshakerResult
|
||||||
|
HandshakerStatus
|
||||||
|
HandshakerResp
|
||||||
|
RpcProtocolVersions
|
||||||
|
*/
|
||||||
|
package grpc_gcp
|
||||||
|
|
||||||
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the proto package it is being compiled against.
|
||||||
|
// A compilation error at this line likely means your copy of the
|
||||||
|
// proto package needs to be updated.
|
||||||
|
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||||
|
|
||||||
|
type AltsContext struct {
|
||||||
|
// The application protocol negotiated for this connection.
|
||||||
|
ApplicationProtocol string `protobuf:"bytes,1,opt,name=application_protocol,json=applicationProtocol" json:"application_protocol,omitempty"`
|
||||||
|
// The record protocol negotiated for this connection.
|
||||||
|
RecordProtocol string `protobuf:"bytes,2,opt,name=record_protocol,json=recordProtocol" json:"record_protocol,omitempty"`
|
||||||
|
// The security level of the created secure channel.
|
||||||
|
SecurityLevel SecurityLevel `protobuf:"varint,3,opt,name=security_level,json=securityLevel,enum=grpc.gcp.SecurityLevel" json:"security_level,omitempty"`
|
||||||
|
// The peer service account.
|
||||||
|
PeerServiceAccount string `protobuf:"bytes,4,opt,name=peer_service_account,json=peerServiceAccount" json:"peer_service_account,omitempty"`
|
||||||
|
// The local service account.
|
||||||
|
LocalServiceAccount string `protobuf:"bytes,5,opt,name=local_service_account,json=localServiceAccount" json:"local_service_account,omitempty"`
|
||||||
|
// The RPC protocol versions supported by the peer.
|
||||||
|
PeerRpcVersions *RpcProtocolVersions `protobuf:"bytes,6,opt,name=peer_rpc_versions,json=peerRpcVersions" json:"peer_rpc_versions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AltsContext) Reset() { *m = AltsContext{} }
|
||||||
|
func (m *AltsContext) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*AltsContext) ProtoMessage() {}
|
||||||
|
func (*AltsContext) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
|
||||||
|
|
||||||
|
func (m *AltsContext) GetApplicationProtocol() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.ApplicationProtocol
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AltsContext) GetRecordProtocol() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.RecordProtocol
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AltsContext) GetSecurityLevel() SecurityLevel {
|
||||||
|
if m != nil {
|
||||||
|
return m.SecurityLevel
|
||||||
|
}
|
||||||
|
return SecurityLevel_SECURITY_NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AltsContext) GetPeerServiceAccount() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.PeerServiceAccount
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AltsContext) GetLocalServiceAccount() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.LocalServiceAccount
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *AltsContext) GetPeerRpcVersions() *RpcProtocolVersions {
|
||||||
|
if m != nil {
|
||||||
|
return m.PeerRpcVersions
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*AltsContext)(nil), "grpc.gcp.AltsContext")
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("altscontext.proto", fileDescriptor0) }
|
||||||
|
|
||||||
|
var fileDescriptor0 = []byte{
|
||||||
|
// 280 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x91, 0x41, 0x4b, 0x33, 0x31,
|
||||||
|
0x10, 0x86, 0xd9, 0x7e, 0x9f, 0x45, 0x53, 0xdd, 0xd2, 0x58, 0x71, 0x11, 0xc4, 0xe2, 0xc5, 0x9e,
|
||||||
|
0x16, 0x5d, 0xef, 0x42, 0xf5, 0x24, 0x78, 0x90, 0x2d, 0x78, 0x0d, 0x71, 0x0c, 0x25, 0x90, 0x66,
|
||||||
|
0xc2, 0x24, 0x5d, 0xf4, 0xaf, 0xfa, 0x6b, 0x64, 0x93, 0xdd, 0xb6, 0xe8, 0x31, 0xf3, 0x3c, 0x6f,
|
||||||
|
0x66, 0x26, 0x61, 0x13, 0x69, 0x82, 0x07, 0xb4, 0x41, 0x7d, 0x86, 0xd2, 0x11, 0x06, 0xe4, 0x87,
|
||||||
|
0x2b, 0x72, 0x50, 0xae, 0xc0, 0x5d, 0x5c, 0x05, 0x92, 0xd6, 0x3b, 0xa4, 0x20, 0xbc, 0x82, 0x0d,
|
||||||
|
0xe9, 0xf0, 0x25, 0x00, 0xd7, 0x6b, 0xb4, 0x49, 0xbd, 0xfe, 0x1e, 0xb0, 0xd1, 0xc2, 0x04, 0xff,
|
||||||
|
0x94, 0x2e, 0xe0, 0x77, 0x6c, 0x2a, 0x9d, 0x33, 0x1a, 0x64, 0xd0, 0x68, 0x45, 0x94, 0x00, 0x4d,
|
||||||
|
0x91, 0xcd, 0xb2, 0xf9, 0x51, 0x7d, 0xba, 0xc7, 0x5e, 0x3b, 0xc4, 0x6f, 0xd8, 0x98, 0x14, 0x20,
|
||||||
|
0x7d, 0xec, 0xec, 0x41, 0xb4, 0xf3, 0x54, 0xde, 0x8a, 0x0f, 0x2c, 0xdf, 0x0e, 0x61, 0x54, 0xa3,
|
||||||
|
0x4c, 0xf1, 0x6f, 0x96, 0xcd, 0xf3, 0xea, 0xbc, 0xec, 0xe7, 0x2d, 0x97, 0x1d, 0x7f, 0x69, 0x71,
|
||||||
|
0x7d, 0xe2, 0xf7, 0x8f, 0xfc, 0x96, 0x4d, 0x9d, 0x52, 0x24, 0xbc, 0xa2, 0x46, 0x83, 0x12, 0x12,
|
||||||
|
0x00, 0x37, 0x36, 0x14, 0xff, 0x63, 0x37, 0xde, 0xb2, 0x65, 0x42, 0x8b, 0x44, 0x78, 0xc5, 0xce,
|
||||||
|
0x0c, 0x82, 0x34, 0x7f, 0x22, 0x07, 0x69, 0x9d, 0x08, 0x7f, 0x65, 0x9e, 0xd9, 0x24, 0x76, 0x21,
|
||||||
|
0x07, 0xa2, 0x51, 0xe4, 0x35, 0x5a, 0x5f, 0x0c, 0x67, 0xd9, 0x7c, 0x54, 0x5d, 0xee, 0x06, 0xad,
|
||||||
|
0x1d, 0xf4, 0x7b, 0xbd, 0x75, 0x52, 0x3d, 0x6e, 0x73, 0xb5, 0x83, 0xbe, 0xf0, 0x98, 0xb3, 0x63,
|
||||||
|
0x8d, 0x29, 0xd3, 0x7e, 0xd2, 0xfb, 0x30, 0x3e, 0xd0, 0xfd, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff,
|
||||||
|
0x04, 0x64, 0x9c, 0x2f, 0xb3, 0x01, 0x00, 0x00,
|
||||||
|
}
|
41
credentials/alts/core/proto/altscontext.proto
Normal file
41
credentials/alts/core/proto/altscontext.proto
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright 2018 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.
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "transport_security_common.proto";
|
||||||
|
|
||||||
|
package grpc.gcp;
|
||||||
|
|
||||||
|
option java_package = "io.grpc.alts";
|
||||||
|
|
||||||
|
message AltsContext {
|
||||||
|
// The application protocol negotiated for this connection.
|
||||||
|
string application_protocol = 1;
|
||||||
|
|
||||||
|
// The record protocol negotiated for this connection.
|
||||||
|
string record_protocol = 2;
|
||||||
|
|
||||||
|
// The security level of the created secure channel.
|
||||||
|
SecurityLevel security_level = 3;
|
||||||
|
|
||||||
|
// The peer service account.
|
||||||
|
string peer_service_account = 4;
|
||||||
|
|
||||||
|
// The local service account.
|
||||||
|
string local_service_account = 5;
|
||||||
|
|
||||||
|
// The RPC protocol versions supported by the peer.
|
||||||
|
RpcProtocolVersions peer_rpc_versions = 6;
|
||||||
|
}
|
933
credentials/alts/core/proto/handshaker.pb.go
Normal file
933
credentials/alts/core/proto/handshaker.pb.go
Normal file
@ -0,0 +1,933 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// source: handshaker.proto
|
||||||
|
|
||||||
|
package grpc_gcp
|
||||||
|
|
||||||
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "golang.org/x/net/context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
type HandshakeProtocol int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Default value.
|
||||||
|
HandshakeProtocol_HANDSHAKE_PROTOCOL_UNSPECIFIED HandshakeProtocol = 0
|
||||||
|
// TLS handshake protocol.
|
||||||
|
HandshakeProtocol_TLS HandshakeProtocol = 1
|
||||||
|
// Application Layer Transport Security handshake protocol.
|
||||||
|
HandshakeProtocol_ALTS HandshakeProtocol = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
var HandshakeProtocol_name = map[int32]string{
|
||||||
|
0: "HANDSHAKE_PROTOCOL_UNSPECIFIED",
|
||||||
|
1: "TLS",
|
||||||
|
2: "ALTS",
|
||||||
|
}
|
||||||
|
var HandshakeProtocol_value = map[string]int32{
|
||||||
|
"HANDSHAKE_PROTOCOL_UNSPECIFIED": 0,
|
||||||
|
"TLS": 1,
|
||||||
|
"ALTS": 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x HandshakeProtocol) String() string {
|
||||||
|
return proto.EnumName(HandshakeProtocol_name, int32(x))
|
||||||
|
}
|
||||||
|
func (HandshakeProtocol) EnumDescriptor() ([]byte, []int) { return fileDescriptor1, []int{0} }
|
||||||
|
|
||||||
|
type NetworkProtocol int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
NetworkProtocol_NETWORK_PROTOCOL_UNSPECIFIED NetworkProtocol = 0
|
||||||
|
NetworkProtocol_TCP NetworkProtocol = 1
|
||||||
|
NetworkProtocol_UDP NetworkProtocol = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
var NetworkProtocol_name = map[int32]string{
|
||||||
|
0: "NETWORK_PROTOCOL_UNSPECIFIED",
|
||||||
|
1: "TCP",
|
||||||
|
2: "UDP",
|
||||||
|
}
|
||||||
|
var NetworkProtocol_value = map[string]int32{
|
||||||
|
"NETWORK_PROTOCOL_UNSPECIFIED": 0,
|
||||||
|
"TCP": 1,
|
||||||
|
"UDP": 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x NetworkProtocol) String() string {
|
||||||
|
return proto.EnumName(NetworkProtocol_name, int32(x))
|
||||||
|
}
|
||||||
|
func (NetworkProtocol) EnumDescriptor() ([]byte, []int) { return fileDescriptor1, []int{1} }
|
||||||
|
|
||||||
|
type Endpoint struct {
|
||||||
|
// IP address. It should contain an IPv4 or IPv6 string literal, e.g.
|
||||||
|
// "192.168.0.1" or "2001:db8::1".
|
||||||
|
IpAddress string `protobuf:"bytes,1,opt,name=ip_address,json=ipAddress" json:"ip_address,omitempty"`
|
||||||
|
// Port number.
|
||||||
|
Port int32 `protobuf:"varint,2,opt,name=port" json:"port,omitempty"`
|
||||||
|
// Network protocol (e.g., TCP, UDP) associated with this endpoint.
|
||||||
|
Protocol NetworkProtocol `protobuf:"varint,3,opt,name=protocol,enum=grpc.gcp.NetworkProtocol" json:"protocol,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Endpoint) Reset() { *m = Endpoint{} }
|
||||||
|
func (m *Endpoint) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Endpoint) ProtoMessage() {}
|
||||||
|
func (*Endpoint) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{0} }
|
||||||
|
|
||||||
|
func (m *Endpoint) GetIpAddress() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.IpAddress
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Endpoint) GetPort() int32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Port
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Endpoint) GetProtocol() NetworkProtocol {
|
||||||
|
if m != nil {
|
||||||
|
return m.Protocol
|
||||||
|
}
|
||||||
|
return NetworkProtocol_NETWORK_PROTOCOL_UNSPECIFIED
|
||||||
|
}
|
||||||
|
|
||||||
|
type Identity struct {
|
||||||
|
// Types that are valid to be assigned to IdentityOneof:
|
||||||
|
// *Identity_ServiceAccount
|
||||||
|
// *Identity_Hostname
|
||||||
|
IdentityOneof isIdentity_IdentityOneof `protobuf_oneof:"identity_oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Identity) Reset() { *m = Identity{} }
|
||||||
|
func (m *Identity) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Identity) ProtoMessage() {}
|
||||||
|
func (*Identity) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{1} }
|
||||||
|
|
||||||
|
type isIdentity_IdentityOneof interface {
|
||||||
|
isIdentity_IdentityOneof()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Identity_ServiceAccount struct {
|
||||||
|
ServiceAccount string `protobuf:"bytes,1,opt,name=service_account,json=serviceAccount,oneof"`
|
||||||
|
}
|
||||||
|
type Identity_Hostname struct {
|
||||||
|
Hostname string `protobuf:"bytes,2,opt,name=hostname,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*Identity_ServiceAccount) isIdentity_IdentityOneof() {}
|
||||||
|
func (*Identity_Hostname) isIdentity_IdentityOneof() {}
|
||||||
|
|
||||||
|
func (m *Identity) GetIdentityOneof() isIdentity_IdentityOneof {
|
||||||
|
if m != nil {
|
||||||
|
return m.IdentityOneof
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Identity) GetServiceAccount() string {
|
||||||
|
if x, ok := m.GetIdentityOneof().(*Identity_ServiceAccount); ok {
|
||||||
|
return x.ServiceAccount
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Identity) GetHostname() string {
|
||||||
|
if x, ok := m.GetIdentityOneof().(*Identity_Hostname); ok {
|
||||||
|
return x.Hostname
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX_OneofFuncs is for the internal use of the proto package.
|
||||||
|
func (*Identity) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
|
||||||
|
return _Identity_OneofMarshaler, _Identity_OneofUnmarshaler, _Identity_OneofSizer, []interface{}{
|
||||||
|
(*Identity_ServiceAccount)(nil),
|
||||||
|
(*Identity_Hostname)(nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Identity_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
|
||||||
|
m := msg.(*Identity)
|
||||||
|
// identity_oneof
|
||||||
|
switch x := m.IdentityOneof.(type) {
|
||||||
|
case *Identity_ServiceAccount:
|
||||||
|
b.EncodeVarint(1<<3 | proto.WireBytes)
|
||||||
|
b.EncodeStringBytes(x.ServiceAccount)
|
||||||
|
case *Identity_Hostname:
|
||||||
|
b.EncodeVarint(2<<3 | proto.WireBytes)
|
||||||
|
b.EncodeStringBytes(x.Hostname)
|
||||||
|
case nil:
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Identity.IdentityOneof has unexpected type %T", x)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Identity_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
|
||||||
|
m := msg.(*Identity)
|
||||||
|
switch tag {
|
||||||
|
case 1: // identity_oneof.service_account
|
||||||
|
if wire != proto.WireBytes {
|
||||||
|
return true, proto.ErrInternalBadWireType
|
||||||
|
}
|
||||||
|
x, err := b.DecodeStringBytes()
|
||||||
|
m.IdentityOneof = &Identity_ServiceAccount{x}
|
||||||
|
return true, err
|
||||||
|
case 2: // identity_oneof.hostname
|
||||||
|
if wire != proto.WireBytes {
|
||||||
|
return true, proto.ErrInternalBadWireType
|
||||||
|
}
|
||||||
|
x, err := b.DecodeStringBytes()
|
||||||
|
m.IdentityOneof = &Identity_Hostname{x}
|
||||||
|
return true, err
|
||||||
|
default:
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _Identity_OneofSizer(msg proto.Message) (n int) {
|
||||||
|
m := msg.(*Identity)
|
||||||
|
// identity_oneof
|
||||||
|
switch x := m.IdentityOneof.(type) {
|
||||||
|
case *Identity_ServiceAccount:
|
||||||
|
n += proto.SizeVarint(1<<3 | proto.WireBytes)
|
||||||
|
n += proto.SizeVarint(uint64(len(x.ServiceAccount)))
|
||||||
|
n += len(x.ServiceAccount)
|
||||||
|
case *Identity_Hostname:
|
||||||
|
n += proto.SizeVarint(2<<3 | proto.WireBytes)
|
||||||
|
n += proto.SizeVarint(uint64(len(x.Hostname)))
|
||||||
|
n += len(x.Hostname)
|
||||||
|
case nil:
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
type StartClientHandshakeReq struct {
|
||||||
|
// Handshake security protocol requested by the client.
|
||||||
|
HandshakeSecurityProtocol HandshakeProtocol `protobuf:"varint,1,opt,name=handshake_security_protocol,json=handshakeSecurityProtocol,enum=grpc.gcp.HandshakeProtocol" json:"handshake_security_protocol,omitempty"`
|
||||||
|
// The application protocols supported by the client, e.g., "h2" (for http2),
|
||||||
|
// "grpc".
|
||||||
|
ApplicationProtocols []string `protobuf:"bytes,2,rep,name=application_protocols,json=applicationProtocols" json:"application_protocols,omitempty"`
|
||||||
|
// The record protocols supported by the client, e.g.,
|
||||||
|
// "ALTSRP_GCM_AES128".
|
||||||
|
RecordProtocols []string `protobuf:"bytes,3,rep,name=record_protocols,json=recordProtocols" json:"record_protocols,omitempty"`
|
||||||
|
// (Optional) Describes which server identities are acceptable by the client.
|
||||||
|
// If target identities are provided and none of them matches the peer
|
||||||
|
// identity of the server, handshake will fail.
|
||||||
|
TargetIdentities []*Identity `protobuf:"bytes,4,rep,name=target_identities,json=targetIdentities" json:"target_identities,omitempty"`
|
||||||
|
// (Optional) Application may specify a local identity. Otherwise, the
|
||||||
|
// handshaker chooses a default local identity.
|
||||||
|
LocalIdentity *Identity `protobuf:"bytes,5,opt,name=local_identity,json=localIdentity" json:"local_identity,omitempty"`
|
||||||
|
// (Optional) Local endpoint information of the connection to the server,
|
||||||
|
// such as local IP address, port number, and network protocol.
|
||||||
|
LocalEndpoint *Endpoint `protobuf:"bytes,6,opt,name=local_endpoint,json=localEndpoint" json:"local_endpoint,omitempty"`
|
||||||
|
// (Optional) Endpoint information of the remote server, such as IP address,
|
||||||
|
// port number, and network protocol.
|
||||||
|
RemoteEndpoint *Endpoint `protobuf:"bytes,7,opt,name=remote_endpoint,json=remoteEndpoint" json:"remote_endpoint,omitempty"`
|
||||||
|
// (Optional) If target name is provided, a secure naming check is performed
|
||||||
|
// to verify that the peer authenticated identity is indeed authorized to run
|
||||||
|
// the target name.
|
||||||
|
TargetName string `protobuf:"bytes,8,opt,name=target_name,json=targetName" json:"target_name,omitempty"`
|
||||||
|
// (Optional) RPC protocol versions supported by the client.
|
||||||
|
RpcVersions *RpcProtocolVersions `protobuf:"bytes,9,opt,name=rpc_versions,json=rpcVersions" json:"rpc_versions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StartClientHandshakeReq) Reset() { *m = StartClientHandshakeReq{} }
|
||||||
|
func (m *StartClientHandshakeReq) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*StartClientHandshakeReq) ProtoMessage() {}
|
||||||
|
func (*StartClientHandshakeReq) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{2} }
|
||||||
|
|
||||||
|
func (m *StartClientHandshakeReq) GetHandshakeSecurityProtocol() HandshakeProtocol {
|
||||||
|
if m != nil {
|
||||||
|
return m.HandshakeSecurityProtocol
|
||||||
|
}
|
||||||
|
return HandshakeProtocol_HANDSHAKE_PROTOCOL_UNSPECIFIED
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StartClientHandshakeReq) GetApplicationProtocols() []string {
|
||||||
|
if m != nil {
|
||||||
|
return m.ApplicationProtocols
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StartClientHandshakeReq) GetRecordProtocols() []string {
|
||||||
|
if m != nil {
|
||||||
|
return m.RecordProtocols
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StartClientHandshakeReq) GetTargetIdentities() []*Identity {
|
||||||
|
if m != nil {
|
||||||
|
return m.TargetIdentities
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StartClientHandshakeReq) GetLocalIdentity() *Identity {
|
||||||
|
if m != nil {
|
||||||
|
return m.LocalIdentity
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StartClientHandshakeReq) GetLocalEndpoint() *Endpoint {
|
||||||
|
if m != nil {
|
||||||
|
return m.LocalEndpoint
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StartClientHandshakeReq) GetRemoteEndpoint() *Endpoint {
|
||||||
|
if m != nil {
|
||||||
|
return m.RemoteEndpoint
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StartClientHandshakeReq) GetTargetName() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.TargetName
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StartClientHandshakeReq) GetRpcVersions() *RpcProtocolVersions {
|
||||||
|
if m != nil {
|
||||||
|
return m.RpcVersions
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerHandshakeParameters struct {
|
||||||
|
// The record protocols supported by the server, e.g.,
|
||||||
|
// "ALTSRP_GCM_AES128".
|
||||||
|
RecordProtocols []string `protobuf:"bytes,1,rep,name=record_protocols,json=recordProtocols" json:"record_protocols,omitempty"`
|
||||||
|
// (Optional) A list of local identities supported by the server, if
|
||||||
|
// specified. Otherwise, the handshaker chooses a default local identity.
|
||||||
|
LocalIdentities []*Identity `protobuf:"bytes,2,rep,name=local_identities,json=localIdentities" json:"local_identities,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServerHandshakeParameters) Reset() { *m = ServerHandshakeParameters{} }
|
||||||
|
func (m *ServerHandshakeParameters) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*ServerHandshakeParameters) ProtoMessage() {}
|
||||||
|
func (*ServerHandshakeParameters) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{3} }
|
||||||
|
|
||||||
|
func (m *ServerHandshakeParameters) GetRecordProtocols() []string {
|
||||||
|
if m != nil {
|
||||||
|
return m.RecordProtocols
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *ServerHandshakeParameters) GetLocalIdentities() []*Identity {
|
||||||
|
if m != nil {
|
||||||
|
return m.LocalIdentities
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type StartServerHandshakeReq struct {
|
||||||
|
// The application protocols supported by the server, e.g., "h2" (for http2),
|
||||||
|
// "grpc".
|
||||||
|
ApplicationProtocols []string `protobuf:"bytes,1,rep,name=application_protocols,json=applicationProtocols" json:"application_protocols,omitempty"`
|
||||||
|
// Handshake parameters (record protocols and local identities supported by
|
||||||
|
// the server) mapped by the handshake protocol. Each handshake security
|
||||||
|
// protocol (e.g., TLS or ALTS) has its own set of record protocols and local
|
||||||
|
// identities. Since protobuf does not support enum as key to the map, the key
|
||||||
|
// to handshake_parameters is the integer value of HandshakeProtocol enum.
|
||||||
|
HandshakeParameters map[int32]*ServerHandshakeParameters `protobuf:"bytes,2,rep,name=handshake_parameters,json=handshakeParameters" json:"handshake_parameters,omitempty" protobuf_key:"varint,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"`
|
||||||
|
// Bytes in out_frames returned from the peer's HandshakerResp. It is possible
|
||||||
|
// that the peer's out_frames are split into multiple HandshakReq messages.
|
||||||
|
InBytes []byte `protobuf:"bytes,3,opt,name=in_bytes,json=inBytes,proto3" json:"in_bytes,omitempty"`
|
||||||
|
// (Optional) Local endpoint information of the connection to the client,
|
||||||
|
// such as local IP address, port number, and network protocol.
|
||||||
|
LocalEndpoint *Endpoint `protobuf:"bytes,4,opt,name=local_endpoint,json=localEndpoint" json:"local_endpoint,omitempty"`
|
||||||
|
// (Optional) Endpoint information of the remote client, such as IP address,
|
||||||
|
// port number, and network protocol.
|
||||||
|
RemoteEndpoint *Endpoint `protobuf:"bytes,5,opt,name=remote_endpoint,json=remoteEndpoint" json:"remote_endpoint,omitempty"`
|
||||||
|
// (Optional) RPC protocol versions supported by the server.
|
||||||
|
RpcVersions *RpcProtocolVersions `protobuf:"bytes,6,opt,name=rpc_versions,json=rpcVersions" json:"rpc_versions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StartServerHandshakeReq) Reset() { *m = StartServerHandshakeReq{} }
|
||||||
|
func (m *StartServerHandshakeReq) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*StartServerHandshakeReq) ProtoMessage() {}
|
||||||
|
func (*StartServerHandshakeReq) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{4} }
|
||||||
|
|
||||||
|
func (m *StartServerHandshakeReq) GetApplicationProtocols() []string {
|
||||||
|
if m != nil {
|
||||||
|
return m.ApplicationProtocols
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StartServerHandshakeReq) GetHandshakeParameters() map[int32]*ServerHandshakeParameters {
|
||||||
|
if m != nil {
|
||||||
|
return m.HandshakeParameters
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StartServerHandshakeReq) GetInBytes() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.InBytes
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StartServerHandshakeReq) GetLocalEndpoint() *Endpoint {
|
||||||
|
if m != nil {
|
||||||
|
return m.LocalEndpoint
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StartServerHandshakeReq) GetRemoteEndpoint() *Endpoint {
|
||||||
|
if m != nil {
|
||||||
|
return m.RemoteEndpoint
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *StartServerHandshakeReq) GetRpcVersions() *RpcProtocolVersions {
|
||||||
|
if m != nil {
|
||||||
|
return m.RpcVersions
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type NextHandshakeMessageReq struct {
|
||||||
|
// Bytes in out_frames returned from the peer's HandshakerResp. It is possible
|
||||||
|
// that the peer's out_frames are split into multiple NextHandshakerMessageReq
|
||||||
|
// messages.
|
||||||
|
InBytes []byte `protobuf:"bytes,1,opt,name=in_bytes,json=inBytes,proto3" json:"in_bytes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *NextHandshakeMessageReq) Reset() { *m = NextHandshakeMessageReq{} }
|
||||||
|
func (m *NextHandshakeMessageReq) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*NextHandshakeMessageReq) ProtoMessage() {}
|
||||||
|
func (*NextHandshakeMessageReq) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{5} }
|
||||||
|
|
||||||
|
func (m *NextHandshakeMessageReq) GetInBytes() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.InBytes
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandshakerReq struct {
|
||||||
|
// Types that are valid to be assigned to ReqOneof:
|
||||||
|
// *HandshakerReq_ClientStart
|
||||||
|
// *HandshakerReq_ServerStart
|
||||||
|
// *HandshakerReq_Next
|
||||||
|
ReqOneof isHandshakerReq_ReqOneof `protobuf_oneof:"req_oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HandshakerReq) Reset() { *m = HandshakerReq{} }
|
||||||
|
func (m *HandshakerReq) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*HandshakerReq) ProtoMessage() {}
|
||||||
|
func (*HandshakerReq) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{6} }
|
||||||
|
|
||||||
|
type isHandshakerReq_ReqOneof interface {
|
||||||
|
isHandshakerReq_ReqOneof()
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandshakerReq_ClientStart struct {
|
||||||
|
ClientStart *StartClientHandshakeReq `protobuf:"bytes,1,opt,name=client_start,json=clientStart,oneof"`
|
||||||
|
}
|
||||||
|
type HandshakerReq_ServerStart struct {
|
||||||
|
ServerStart *StartServerHandshakeReq `protobuf:"bytes,2,opt,name=server_start,json=serverStart,oneof"`
|
||||||
|
}
|
||||||
|
type HandshakerReq_Next struct {
|
||||||
|
Next *NextHandshakeMessageReq `protobuf:"bytes,3,opt,name=next,oneof"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*HandshakerReq_ClientStart) isHandshakerReq_ReqOneof() {}
|
||||||
|
func (*HandshakerReq_ServerStart) isHandshakerReq_ReqOneof() {}
|
||||||
|
func (*HandshakerReq_Next) isHandshakerReq_ReqOneof() {}
|
||||||
|
|
||||||
|
func (m *HandshakerReq) GetReqOneof() isHandshakerReq_ReqOneof {
|
||||||
|
if m != nil {
|
||||||
|
return m.ReqOneof
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HandshakerReq) GetClientStart() *StartClientHandshakeReq {
|
||||||
|
if x, ok := m.GetReqOneof().(*HandshakerReq_ClientStart); ok {
|
||||||
|
return x.ClientStart
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HandshakerReq) GetServerStart() *StartServerHandshakeReq {
|
||||||
|
if x, ok := m.GetReqOneof().(*HandshakerReq_ServerStart); ok {
|
||||||
|
return x.ServerStart
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HandshakerReq) GetNext() *NextHandshakeMessageReq {
|
||||||
|
if x, ok := m.GetReqOneof().(*HandshakerReq_Next); ok {
|
||||||
|
return x.Next
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX_OneofFuncs is for the internal use of the proto package.
|
||||||
|
func (*HandshakerReq) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
|
||||||
|
return _HandshakerReq_OneofMarshaler, _HandshakerReq_OneofUnmarshaler, _HandshakerReq_OneofSizer, []interface{}{
|
||||||
|
(*HandshakerReq_ClientStart)(nil),
|
||||||
|
(*HandshakerReq_ServerStart)(nil),
|
||||||
|
(*HandshakerReq_Next)(nil),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _HandshakerReq_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
|
||||||
|
m := msg.(*HandshakerReq)
|
||||||
|
// req_oneof
|
||||||
|
switch x := m.ReqOneof.(type) {
|
||||||
|
case *HandshakerReq_ClientStart:
|
||||||
|
b.EncodeVarint(1<<3 | proto.WireBytes)
|
||||||
|
if err := b.EncodeMessage(x.ClientStart); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *HandshakerReq_ServerStart:
|
||||||
|
b.EncodeVarint(2<<3 | proto.WireBytes)
|
||||||
|
if err := b.EncodeMessage(x.ServerStart); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *HandshakerReq_Next:
|
||||||
|
b.EncodeVarint(3<<3 | proto.WireBytes)
|
||||||
|
if err := b.EncodeMessage(x.Next); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case nil:
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("HandshakerReq.ReqOneof has unexpected type %T", x)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func _HandshakerReq_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
|
||||||
|
m := msg.(*HandshakerReq)
|
||||||
|
switch tag {
|
||||||
|
case 1: // req_oneof.client_start
|
||||||
|
if wire != proto.WireBytes {
|
||||||
|
return true, proto.ErrInternalBadWireType
|
||||||
|
}
|
||||||
|
msg := new(StartClientHandshakeReq)
|
||||||
|
err := b.DecodeMessage(msg)
|
||||||
|
m.ReqOneof = &HandshakerReq_ClientStart{msg}
|
||||||
|
return true, err
|
||||||
|
case 2: // req_oneof.server_start
|
||||||
|
if wire != proto.WireBytes {
|
||||||
|
return true, proto.ErrInternalBadWireType
|
||||||
|
}
|
||||||
|
msg := new(StartServerHandshakeReq)
|
||||||
|
err := b.DecodeMessage(msg)
|
||||||
|
m.ReqOneof = &HandshakerReq_ServerStart{msg}
|
||||||
|
return true, err
|
||||||
|
case 3: // req_oneof.next
|
||||||
|
if wire != proto.WireBytes {
|
||||||
|
return true, proto.ErrInternalBadWireType
|
||||||
|
}
|
||||||
|
msg := new(NextHandshakeMessageReq)
|
||||||
|
err := b.DecodeMessage(msg)
|
||||||
|
m.ReqOneof = &HandshakerReq_Next{msg}
|
||||||
|
return true, err
|
||||||
|
default:
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _HandshakerReq_OneofSizer(msg proto.Message) (n int) {
|
||||||
|
m := msg.(*HandshakerReq)
|
||||||
|
// req_oneof
|
||||||
|
switch x := m.ReqOneof.(type) {
|
||||||
|
case *HandshakerReq_ClientStart:
|
||||||
|
s := proto.Size(x.ClientStart)
|
||||||
|
n += proto.SizeVarint(1<<3 | proto.WireBytes)
|
||||||
|
n += proto.SizeVarint(uint64(s))
|
||||||
|
n += s
|
||||||
|
case *HandshakerReq_ServerStart:
|
||||||
|
s := proto.Size(x.ServerStart)
|
||||||
|
n += proto.SizeVarint(2<<3 | proto.WireBytes)
|
||||||
|
n += proto.SizeVarint(uint64(s))
|
||||||
|
n += s
|
||||||
|
case *HandshakerReq_Next:
|
||||||
|
s := proto.Size(x.Next)
|
||||||
|
n += proto.SizeVarint(3<<3 | proto.WireBytes)
|
||||||
|
n += proto.SizeVarint(uint64(s))
|
||||||
|
n += s
|
||||||
|
case nil:
|
||||||
|
default:
|
||||||
|
panic(fmt.Sprintf("proto: unexpected type %T in oneof", x))
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandshakerResult struct {
|
||||||
|
// The application protocol negotiated for this connection.
|
||||||
|
ApplicationProtocol string `protobuf:"bytes,1,opt,name=application_protocol,json=applicationProtocol" json:"application_protocol,omitempty"`
|
||||||
|
// The record protocol negotiated for this connection.
|
||||||
|
RecordProtocol string `protobuf:"bytes,2,opt,name=record_protocol,json=recordProtocol" json:"record_protocol,omitempty"`
|
||||||
|
// Cryptographic key data. The key data may be more than the key length
|
||||||
|
// required for the record protocol, thus the client of the handshaker
|
||||||
|
// service needs to truncate the key data into the right key length.
|
||||||
|
KeyData []byte `protobuf:"bytes,3,opt,name=key_data,json=keyData,proto3" json:"key_data,omitempty"`
|
||||||
|
// The authenticated identity of the peer.
|
||||||
|
PeerIdentity *Identity `protobuf:"bytes,4,opt,name=peer_identity,json=peerIdentity" json:"peer_identity,omitempty"`
|
||||||
|
// The local identity used in the handshake.
|
||||||
|
LocalIdentity *Identity `protobuf:"bytes,5,opt,name=local_identity,json=localIdentity" json:"local_identity,omitempty"`
|
||||||
|
// Indicate whether the handshaker service client should keep the channel
|
||||||
|
// between the handshaker service open, e.g., in order to handle
|
||||||
|
// post-handshake messages in the future.
|
||||||
|
KeepChannelOpen bool `protobuf:"varint,6,opt,name=keep_channel_open,json=keepChannelOpen" json:"keep_channel_open,omitempty"`
|
||||||
|
// The RPC protocol versions supported by the peer.
|
||||||
|
PeerRpcVersions *RpcProtocolVersions `protobuf:"bytes,7,opt,name=peer_rpc_versions,json=peerRpcVersions" json:"peer_rpc_versions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HandshakerResult) Reset() { *m = HandshakerResult{} }
|
||||||
|
func (m *HandshakerResult) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*HandshakerResult) ProtoMessage() {}
|
||||||
|
func (*HandshakerResult) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{7} }
|
||||||
|
|
||||||
|
func (m *HandshakerResult) GetApplicationProtocol() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.ApplicationProtocol
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HandshakerResult) GetRecordProtocol() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.RecordProtocol
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HandshakerResult) GetKeyData() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.KeyData
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HandshakerResult) GetPeerIdentity() *Identity {
|
||||||
|
if m != nil {
|
||||||
|
return m.PeerIdentity
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HandshakerResult) GetLocalIdentity() *Identity {
|
||||||
|
if m != nil {
|
||||||
|
return m.LocalIdentity
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HandshakerResult) GetKeepChannelOpen() bool {
|
||||||
|
if m != nil {
|
||||||
|
return m.KeepChannelOpen
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HandshakerResult) GetPeerRpcVersions() *RpcProtocolVersions {
|
||||||
|
if m != nil {
|
||||||
|
return m.PeerRpcVersions
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandshakerStatus struct {
|
||||||
|
// The status code. This could be the gRPC status code.
|
||||||
|
Code uint32 `protobuf:"varint,1,opt,name=code" json:"code,omitempty"`
|
||||||
|
// The status details.
|
||||||
|
Details string `protobuf:"bytes,2,opt,name=details" json:"details,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HandshakerStatus) Reset() { *m = HandshakerStatus{} }
|
||||||
|
func (m *HandshakerStatus) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*HandshakerStatus) ProtoMessage() {}
|
||||||
|
func (*HandshakerStatus) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{8} }
|
||||||
|
|
||||||
|
func (m *HandshakerStatus) GetCode() uint32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Code
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HandshakerStatus) GetDetails() string {
|
||||||
|
if m != nil {
|
||||||
|
return m.Details
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandshakerResp struct {
|
||||||
|
// Frames to be given to the peer for the NextHandshakeMessageReq. May be
|
||||||
|
// empty if no out_frames have to be sent to the peer or if in_bytes in the
|
||||||
|
// HandshakerReq are incomplete. All the non-empty out frames must be sent to
|
||||||
|
// the peer even if the handshaker status is not OK as these frames may
|
||||||
|
// contain the alert frames.
|
||||||
|
OutFrames []byte `protobuf:"bytes,1,opt,name=out_frames,json=outFrames,proto3" json:"out_frames,omitempty"`
|
||||||
|
// Number of bytes in the in_bytes consumed by the handshaker. It is possible
|
||||||
|
// that part of in_bytes in HandshakerReq was unrelated to the handshake
|
||||||
|
// process.
|
||||||
|
BytesConsumed uint32 `protobuf:"varint,2,opt,name=bytes_consumed,json=bytesConsumed" json:"bytes_consumed,omitempty"`
|
||||||
|
// This is set iff the handshake was successful. out_frames may still be set
|
||||||
|
// to frames that needs to be forwarded to the peer.
|
||||||
|
Result *HandshakerResult `protobuf:"bytes,3,opt,name=result" json:"result,omitempty"`
|
||||||
|
// Status of the handshaker.
|
||||||
|
Status *HandshakerStatus `protobuf:"bytes,4,opt,name=status" json:"status,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HandshakerResp) Reset() { *m = HandshakerResp{} }
|
||||||
|
func (m *HandshakerResp) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*HandshakerResp) ProtoMessage() {}
|
||||||
|
func (*HandshakerResp) Descriptor() ([]byte, []int) { return fileDescriptor1, []int{9} }
|
||||||
|
|
||||||
|
func (m *HandshakerResp) GetOutFrames() []byte {
|
||||||
|
if m != nil {
|
||||||
|
return m.OutFrames
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HandshakerResp) GetBytesConsumed() uint32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.BytesConsumed
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HandshakerResp) GetResult() *HandshakerResult {
|
||||||
|
if m != nil {
|
||||||
|
return m.Result
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *HandshakerResp) GetStatus() *HandshakerStatus {
|
||||||
|
if m != nil {
|
||||||
|
return m.Status
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*Endpoint)(nil), "grpc.gcp.Endpoint")
|
||||||
|
proto.RegisterType((*Identity)(nil), "grpc.gcp.Identity")
|
||||||
|
proto.RegisterType((*StartClientHandshakeReq)(nil), "grpc.gcp.StartClientHandshakeReq")
|
||||||
|
proto.RegisterType((*ServerHandshakeParameters)(nil), "grpc.gcp.ServerHandshakeParameters")
|
||||||
|
proto.RegisterType((*StartServerHandshakeReq)(nil), "grpc.gcp.StartServerHandshakeReq")
|
||||||
|
proto.RegisterType((*NextHandshakeMessageReq)(nil), "grpc.gcp.NextHandshakeMessageReq")
|
||||||
|
proto.RegisterType((*HandshakerReq)(nil), "grpc.gcp.HandshakerReq")
|
||||||
|
proto.RegisterType((*HandshakerResult)(nil), "grpc.gcp.HandshakerResult")
|
||||||
|
proto.RegisterType((*HandshakerStatus)(nil), "grpc.gcp.HandshakerStatus")
|
||||||
|
proto.RegisterType((*HandshakerResp)(nil), "grpc.gcp.HandshakerResp")
|
||||||
|
proto.RegisterEnum("grpc.gcp.HandshakeProtocol", HandshakeProtocol_name, HandshakeProtocol_value)
|
||||||
|
proto.RegisterEnum("grpc.gcp.NetworkProtocol", NetworkProtocol_name, NetworkProtocol_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ context.Context
|
||||||
|
var _ grpc.ClientConn
|
||||||
|
|
||||||
|
// This is a compile-time assertion to ensure that this generated file
|
||||||
|
// is compatible with the grpc package it is being compiled against.
|
||||||
|
const _ = grpc.SupportPackageIsVersion4
|
||||||
|
|
||||||
|
// Client API for HandshakerService service
|
||||||
|
|
||||||
|
type HandshakerServiceClient interface {
|
||||||
|
// Accepts a stream of handshaker request, returning a stream of handshaker
|
||||||
|
// response.
|
||||||
|
DoHandshake(ctx context.Context, opts ...grpc.CallOption) (HandshakerService_DoHandshakeClient, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type handshakerServiceClient struct {
|
||||||
|
cc *grpc.ClientConn
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandshakerServiceClient(cc *grpc.ClientConn) HandshakerServiceClient {
|
||||||
|
return &handshakerServiceClient{cc}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *handshakerServiceClient) DoHandshake(ctx context.Context, opts ...grpc.CallOption) (HandshakerService_DoHandshakeClient, error) {
|
||||||
|
stream, err := grpc.NewClientStream(ctx, &_HandshakerService_serviceDesc.Streams[0], c.cc, "/grpc.gcp.HandshakerService/DoHandshake", opts...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
x := &handshakerServiceDoHandshakeClient{stream}
|
||||||
|
return x, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandshakerService_DoHandshakeClient interface {
|
||||||
|
Send(*HandshakerReq) error
|
||||||
|
Recv() (*HandshakerResp, error)
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type handshakerServiceDoHandshakeClient struct {
|
||||||
|
grpc.ClientStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *handshakerServiceDoHandshakeClient) Send(m *HandshakerReq) error {
|
||||||
|
return x.ClientStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *handshakerServiceDoHandshakeClient) Recv() (*HandshakerResp, error) {
|
||||||
|
m := new(HandshakerResp)
|
||||||
|
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server API for HandshakerService service
|
||||||
|
|
||||||
|
type HandshakerServiceServer interface {
|
||||||
|
// Accepts a stream of handshaker request, returning a stream of handshaker
|
||||||
|
// response.
|
||||||
|
DoHandshake(HandshakerService_DoHandshakeServer) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func RegisterHandshakerServiceServer(s *grpc.Server, srv HandshakerServiceServer) {
|
||||||
|
s.RegisterService(&_HandshakerService_serviceDesc, srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func _HandshakerService_DoHandshake_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||||
|
return srv.(HandshakerServiceServer).DoHandshake(&handshakerServiceDoHandshakeServer{stream})
|
||||||
|
}
|
||||||
|
|
||||||
|
type HandshakerService_DoHandshakeServer interface {
|
||||||
|
Send(*HandshakerResp) error
|
||||||
|
Recv() (*HandshakerReq, error)
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
type handshakerServiceDoHandshakeServer struct {
|
||||||
|
grpc.ServerStream
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *handshakerServiceDoHandshakeServer) Send(m *HandshakerResp) error {
|
||||||
|
return x.ServerStream.SendMsg(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x *handshakerServiceDoHandshakeServer) Recv() (*HandshakerReq, error) {
|
||||||
|
m := new(HandshakerReq)
|
||||||
|
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _HandshakerService_serviceDesc = grpc.ServiceDesc{
|
||||||
|
ServiceName: "grpc.gcp.HandshakerService",
|
||||||
|
HandlerType: (*HandshakerServiceServer)(nil),
|
||||||
|
Methods: []grpc.MethodDesc{},
|
||||||
|
Streams: []grpc.StreamDesc{
|
||||||
|
{
|
||||||
|
StreamName: "DoHandshake",
|
||||||
|
Handler: _HandshakerService_DoHandshake_Handler,
|
||||||
|
ServerStreams: true,
|
||||||
|
ClientStreams: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Metadata: "handshaker.proto",
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("handshaker.proto", fileDescriptor1) }
|
||||||
|
|
||||||
|
var fileDescriptor1 = []byte{
|
||||||
|
// 1066 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x56, 0xdd, 0x6e, 0x1a, 0x47,
|
||||||
|
0x14, 0xf6, 0x02, 0xb6, 0xe1, 0x60, 0x60, 0x3d, 0x71, 0x65, 0xec, 0x24, 0x0d, 0xa5, 0xaa, 0x4a,
|
||||||
|
0x7c, 0x61, 0xb5, 0xa4, 0x55, 0x9a, 0x54, 0x55, 0x63, 0x63, 0x2c, 0xdc, 0xb8, 0xd8, 0x1a, 0x9c,
|
||||||
|
0xf6, 0x22, 0x17, 0xab, 0xc9, 0x72, 0x62, 0xaf, 0x80, 0x99, 0xf5, 0xcc, 0xe0, 0x86, 0x07, 0xe8,
|
||||||
|
0xe3, 0xf4, 0x15, 0xfa, 0x36, 0x7d, 0x83, 0xde, 0xb7, 0xda, 0xd9, 0x3f, 0x8c, 0x97, 0x28, 0x51,
|
||||||
|
0xee, 0x76, 0xcf, 0x7e, 0xdf, 0xd9, 0x39, 0xdf, 0xf9, 0xe6, 0xcc, 0x80, 0x7d, 0xc5, 0xf8, 0x50,
|
||||||
|
0x5d, 0xb1, 0x11, 0xca, 0x7d, 0x5f, 0x0a, 0x2d, 0x48, 0xf1, 0x52, 0xfa, 0xee, 0xfe, 0xa5, 0xeb,
|
||||||
|
0xef, 0x3e, 0xd2, 0x92, 0x71, 0xe5, 0x0b, 0xa9, 0x1d, 0x85, 0xee, 0x54, 0x7a, 0x7a, 0xe6, 0xb8,
|
||||||
|
0x62, 0x32, 0x11, 0x3c, 0x84, 0x36, 0x35, 0x14, 0xbb, 0x7c, 0xe8, 0x0b, 0x8f, 0x6b, 0xf2, 0x10,
|
||||||
|
0xc0, 0xf3, 0x1d, 0x36, 0x1c, 0x4a, 0x54, 0xaa, 0x6e, 0x35, 0xac, 0x56, 0x89, 0x96, 0x3c, 0xff,
|
||||||
|
0x20, 0x0c, 0x10, 0x02, 0x85, 0x20, 0x51, 0x3d, 0xd7, 0xb0, 0x5a, 0xab, 0xd4, 0x3c, 0x93, 0xef,
|
||||||
|
0xa1, 0x68, 0xf2, 0xb8, 0x62, 0x5c, 0xcf, 0x37, 0xac, 0x56, 0xb5, 0xbd, 0xb3, 0x1f, 0xff, 0x7c,
|
||||||
|
0xbf, 0x8f, 0xfa, 0x0f, 0x21, 0x47, 0xe7, 0x11, 0x80, 0x26, 0xd0, 0x26, 0x42, 0xf1, 0x64, 0x88,
|
||||||
|
0x5c, 0x7b, 0x7a, 0x46, 0x1e, 0x43, 0x4d, 0xa1, 0xbc, 0xf1, 0x5c, 0x74, 0x98, 0xeb, 0x8a, 0x29,
|
||||||
|
0xd7, 0xe1, 0xaf, 0x7b, 0x2b, 0xb4, 0x1a, 0x7d, 0x38, 0x08, 0xe3, 0xe4, 0x01, 0x14, 0xaf, 0x84,
|
||||||
|
0xd2, 0x9c, 0x4d, 0xd0, 0xac, 0x22, 0xc0, 0x24, 0x91, 0x43, 0x1b, 0xaa, 0x5e, 0x94, 0xd4, 0x11,
|
||||||
|
0x1c, 0xc5, 0xdb, 0xe6, 0x5f, 0x05, 0xd8, 0x1e, 0x68, 0x26, 0x75, 0x67, 0xec, 0x21, 0xd7, 0xbd,
|
||||||
|
0x58, 0x27, 0x8a, 0xd7, 0xe4, 0x35, 0xdc, 0x4f, 0x74, 0x4b, 0xb5, 0x49, 0x8a, 0xb1, 0x4c, 0x31,
|
||||||
|
0xf7, 0xd3, 0x62, 0x12, 0x72, 0x52, 0xce, 0x4e, 0xc2, 0x1f, 0x44, 0xf4, 0xf8, 0x13, 0x79, 0x02,
|
||||||
|
0x9f, 0x31, 0xdf, 0x1f, 0x7b, 0x2e, 0xd3, 0x9e, 0xe0, 0x49, 0x56, 0x55, 0xcf, 0x35, 0xf2, 0xad,
|
||||||
|
0x12, 0xdd, 0x9a, 0xfb, 0x18, 0x73, 0x14, 0x79, 0x0c, 0xb6, 0x44, 0x57, 0xc8, 0xe1, 0x1c, 0x3e,
|
||||||
|
0x6f, 0xf0, 0xb5, 0x30, 0x9e, 0x42, 0x7f, 0x86, 0x4d, 0xcd, 0xe4, 0x25, 0x6a, 0x27, 0xaa, 0xd8,
|
||||||
|
0x43, 0x55, 0x2f, 0x34, 0xf2, 0xad, 0x72, 0x9b, 0xa4, 0x4b, 0x8e, 0x25, 0xa6, 0x76, 0x08, 0x3e,
|
||||||
|
0x49, 0xb0, 0xe4, 0x19, 0x54, 0xc7, 0xc2, 0x65, 0xe3, 0x98, 0x3f, 0xab, 0xaf, 0x36, 0xac, 0x25,
|
||||||
|
0xec, 0x8a, 0x41, 0x26, 0xfd, 0x4a, 0xa8, 0x18, 0xf9, 0xa6, 0xbe, 0xb6, 0x48, 0x8d, 0x1d, 0x15,
|
||||||
|
0x51, 0x13, 0x83, 0xfd, 0x08, 0x35, 0x89, 0x13, 0xa1, 0x31, 0xe5, 0xae, 0x2f, 0xe5, 0x56, 0x43,
|
||||||
|
0x68, 0x42, 0x7e, 0x04, 0xe5, 0xa8, 0x66, 0xd3, 0xff, 0xa2, 0xb1, 0x27, 0x84, 0xa1, 0x3e, 0x9b,
|
||||||
|
0x20, 0x79, 0x01, 0x1b, 0xd2, 0x77, 0x9d, 0x1b, 0x94, 0xca, 0x13, 0x5c, 0xd5, 0x4b, 0x26, 0xf5,
|
||||||
|
0xc3, 0x34, 0x35, 0xf5, 0xdd, 0x58, 0xc2, 0xdf, 0x22, 0x10, 0x2d, 0x4b, 0xdf, 0x8d, 0x5f, 0x9a,
|
||||||
|
0x7f, 0x5a, 0xb0, 0x33, 0x40, 0x79, 0x83, 0x32, 0xed, 0x36, 0x93, 0x6c, 0x82, 0x1a, 0x65, 0x76,
|
||||||
|
0x7f, 0xac, 0xec, 0xfe, 0xfc, 0x04, 0xf6, 0x2d, 0x79, 0x83, 0xf6, 0xe4, 0x96, 0xb6, 0xa7, 0x36,
|
||||||
|
0x2f, 0xb0, 0x87, 0xaa, 0xf9, 0x5f, 0x3e, 0xf2, 0xed, 0xc2, 0x62, 0x02, 0xdf, 0x2e, 0xb5, 0x96,
|
||||||
|
0xf5, 0x1e, 0x6b, 0x4d, 0x60, 0x2b, 0x35, 0xbb, 0x9f, 0x94, 0x14, 0xad, 0xe9, 0x79, 0xba, 0xa6,
|
||||||
|
0x25, 0x7f, 0xdd, 0xcf, 0xd0, 0xa3, 0xcb, 0xb5, 0x9c, 0xd1, 0x7b, 0x57, 0x19, 0x4a, 0xed, 0x40,
|
||||||
|
0xd1, 0xe3, 0xce, 0x9b, 0x99, 0x46, 0x65, 0xa6, 0xc2, 0x06, 0x5d, 0xf7, 0xf8, 0x61, 0xf0, 0x9a,
|
||||||
|
0xe1, 0x9e, 0xc2, 0x27, 0xb8, 0x67, 0xf5, 0x83, 0xdd, 0xb3, 0x68, 0x8e, 0xb5, 0x8f, 0x35, 0xc7,
|
||||||
|
0xee, 0x08, 0xea, 0xcb, 0x54, 0x20, 0x36, 0xe4, 0x47, 0x38, 0x33, 0x43, 0x63, 0x95, 0x06, 0x8f,
|
||||||
|
0xe4, 0x19, 0xac, 0xde, 0xb0, 0xf1, 0x34, 0x9c, 0x53, 0xe5, 0xf6, 0x97, 0x73, 0x12, 0x2f, 0x33,
|
||||||
|
0x18, 0x0d, 0x19, 0xcf, 0x73, 0x3f, 0x58, 0xcd, 0xef, 0x60, 0xbb, 0x8f, 0xef, 0xd2, 0x89, 0xf5,
|
||||||
|
0x2b, 0x2a, 0xc5, 0x2e, 0x8d, 0x01, 0xe6, 0xc5, 0xb5, 0x6e, 0x89, 0xdb, 0xfc, 0xc7, 0x82, 0x4a,
|
||||||
|
0x42, 0x91, 0x01, 0xf8, 0x18, 0x36, 0x5c, 0x33, 0xfb, 0x1c, 0x15, 0x74, 0xd6, 0x10, 0xca, 0xed,
|
||||||
|
0x2f, 0x16, 0x1a, 0x7e, 0x77, 0x3c, 0xf6, 0x56, 0x68, 0x39, 0x24, 0x1a, 0x40, 0x90, 0x47, 0x99,
|
||||||
|
0x75, 0x47, 0x79, 0x72, 0x99, 0x79, 0xee, 0x1a, 0x27, 0xc8, 0x13, 0x12, 0xc3, 0x3c, 0x4f, 0xa1,
|
||||||
|
0xc0, 0xf1, 0x9d, 0x36, 0xae, 0xb8, 0xc5, 0x5f, 0x52, 0x6d, 0x6f, 0x85, 0x1a, 0xc2, 0x61, 0x19,
|
||||||
|
0x4a, 0x12, 0xaf, 0xa3, 0xb9, 0xfe, 0x6f, 0x0e, 0xec, 0xf9, 0x3a, 0xd5, 0x74, 0xac, 0xc9, 0xb7,
|
||||||
|
0xb0, 0x95, 0xb5, 0x31, 0xa2, 0x73, 0xec, 0x5e, 0xc6, 0xbe, 0x20, 0x5f, 0x43, 0x6d, 0x61, 0x47,
|
||||||
|
0x87, 0xc7, 0x4a, 0xe0, 0x9e, 0xf9, 0x0d, 0x1d, 0x68, 0x3e, 0xc2, 0x99, 0x33, 0x64, 0x9a, 0xc5,
|
||||||
|
0x86, 0x1e, 0xe1, 0xec, 0x88, 0x69, 0x46, 0x9e, 0x42, 0xc5, 0x47, 0x94, 0xe9, 0x20, 0x2d, 0x2c,
|
||||||
|
0x1d, 0xa4, 0x1b, 0x01, 0xf0, 0xee, 0x1c, 0xfd, 0xf8, 0x11, 0xbc, 0x07, 0x9b, 0x23, 0x44, 0xdf,
|
||||||
|
0x71, 0xaf, 0x18, 0xe7, 0x38, 0x76, 0x84, 0x8f, 0xdc, 0x38, 0xba, 0x48, 0x6b, 0xc1, 0x87, 0x4e,
|
||||||
|
0x18, 0x3f, 0xf3, 0x91, 0x93, 0x13, 0xd8, 0x34, 0xeb, 0xbb, 0xe5, 0xfe, 0xf5, 0x0f, 0x71, 0x7f,
|
||||||
|
0x2d, 0xe0, 0xd1, 0xb9, 0xf1, 0xf8, 0x62, 0x5e, 0xf5, 0x81, 0x66, 0x7a, 0x6a, 0x2e, 0x05, 0xae,
|
||||||
|
0x18, 0xa2, 0x51, 0xb9, 0x42, 0xcd, 0x33, 0xa9, 0xc3, 0xfa, 0x10, 0x35, 0xf3, 0xcc, 0x79, 0x17,
|
||||||
|
0xc8, 0x19, 0xbf, 0x36, 0xff, 0xb6, 0xa0, 0x7a, 0xab, 0x71, 0x7e, 0x70, 0xe9, 0x10, 0x53, 0xed,
|
||||||
|
0xbc, 0x0d, 0x76, 0x41, 0x6c, 0xe8, 0x92, 0x98, 0xea, 0x63, 0x13, 0x20, 0x5f, 0x41, 0xd5, 0x58,
|
||||||
|
0xdd, 0x71, 0x05, 0x57, 0xd3, 0x09, 0x0e, 0x4d, 0xca, 0x0a, 0xad, 0x98, 0x68, 0x27, 0x0a, 0x92,
|
||||||
|
0x36, 0xac, 0x49, 0x63, 0x83, 0xc8, 0x59, 0xbb, 0x19, 0x07, 0x77, 0x64, 0x14, 0x1a, 0x21, 0x03,
|
||||||
|
0x8e, 0x32, 0x45, 0x44, 0x2d, 0xcb, 0xe4, 0x84, 0x65, 0xd2, 0x08, 0xb9, 0xf7, 0x0b, 0x6c, 0xde,
|
||||||
|
0xb9, 0x08, 0x90, 0x26, 0x7c, 0xde, 0x3b, 0xe8, 0x1f, 0x0d, 0x7a, 0x07, 0x2f, 0xbb, 0xce, 0x39,
|
||||||
|
0x3d, 0xbb, 0x38, 0xeb, 0x9c, 0x9d, 0x3a, 0xaf, 0xfa, 0x83, 0xf3, 0x6e, 0xe7, 0xe4, 0xf8, 0xa4,
|
||||||
|
0x7b, 0x64, 0xaf, 0x90, 0x75, 0xc8, 0x5f, 0x9c, 0x0e, 0x6c, 0x8b, 0x14, 0xa1, 0x70, 0x70, 0x7a,
|
||||||
|
0x31, 0xb0, 0x73, 0x7b, 0x5d, 0xa8, 0x2d, 0xdc, 0x90, 0x48, 0x03, 0x1e, 0xf4, 0xbb, 0x17, 0xbf,
|
||||||
|
0x9f, 0xd1, 0x97, 0xef, 0xcb, 0xd3, 0x39, 0xb7, 0xad, 0xe0, 0xe1, 0xd5, 0xd1, 0xb9, 0x9d, 0x6b,
|
||||||
|
0xbf, 0x9e, 0x5b, 0x92, 0x1c, 0x84, 0x17, 0x26, 0x72, 0x0c, 0xe5, 0x23, 0x91, 0x84, 0xc9, 0x76,
|
||||||
|
0xb6, 0x1c, 0xd7, 0xbb, 0xf5, 0x25, 0x3a, 0xf9, 0xcd, 0x95, 0x96, 0xf5, 0x8d, 0x75, 0x58, 0x85,
|
||||||
|
0x0d, 0x4f, 0x84, 0x18, 0x36, 0xd6, 0xea, 0xcd, 0x9a, 0xd9, 0x28, 0x4f, 0xfe, 0x0f, 0x00, 0x00,
|
||||||
|
0xff, 0xff, 0x3f, 0xb3, 0x37, 0x22, 0x74, 0x0a, 0x00, 0x00,
|
||||||
|
}
|
220
credentials/alts/core/proto/handshaker.proto
Normal file
220
credentials/alts/core/proto/handshaker.proto
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
// Copyright 2018 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.
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
import "transport_security_common.proto";
|
||||||
|
|
||||||
|
package grpc.gcp;
|
||||||
|
|
||||||
|
option java_package = "io.grpc.alts";
|
||||||
|
|
||||||
|
enum HandshakeProtocol {
|
||||||
|
// Default value.
|
||||||
|
HANDSHAKE_PROTOCOL_UNSPECIFIED = 0;
|
||||||
|
|
||||||
|
// TLS handshake protocol.
|
||||||
|
TLS = 1;
|
||||||
|
|
||||||
|
// Application Layer Transport Security handshake protocol.
|
||||||
|
ALTS = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum NetworkProtocol {
|
||||||
|
NETWORK_PROTOCOL_UNSPECIFIED = 0;
|
||||||
|
TCP = 1;
|
||||||
|
UDP = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Endpoint {
|
||||||
|
// IP address. It should contain an IPv4 or IPv6 string literal, e.g.
|
||||||
|
// "192.168.0.1" or "2001:db8::1".
|
||||||
|
string ip_address = 1;
|
||||||
|
|
||||||
|
// Port number.
|
||||||
|
int32 port = 2;
|
||||||
|
|
||||||
|
// Network protocol (e.g., TCP, UDP) associated with this endpoint.
|
||||||
|
NetworkProtocol protocol = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Identity {
|
||||||
|
oneof identity_oneof {
|
||||||
|
// Service account of a connection endpoint.
|
||||||
|
string service_account = 1;
|
||||||
|
|
||||||
|
// Hostname of a connection endpoint.
|
||||||
|
string hostname = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message StartClientHandshakeReq {
|
||||||
|
// Handshake security protocol requested by the client.
|
||||||
|
HandshakeProtocol handshake_security_protocol = 1;
|
||||||
|
|
||||||
|
// The application protocols supported by the client, e.g., "h2" (for http2),
|
||||||
|
// "grpc".
|
||||||
|
repeated string application_protocols = 2;
|
||||||
|
|
||||||
|
// The record protocols supported by the client, e.g.,
|
||||||
|
// "ALTSRP_GCM_AES128".
|
||||||
|
repeated string record_protocols = 3;
|
||||||
|
|
||||||
|
// (Optional) Describes which server identities are acceptable by the client.
|
||||||
|
// If target identities are provided and none of them matches the peer
|
||||||
|
// identity of the server, handshake will fail.
|
||||||
|
repeated Identity target_identities = 4;
|
||||||
|
|
||||||
|
// (Optional) Application may specify a local identity. Otherwise, the
|
||||||
|
// handshaker chooses a default local identity.
|
||||||
|
Identity local_identity = 5;
|
||||||
|
|
||||||
|
// (Optional) Local endpoint information of the connection to the server,
|
||||||
|
// such as local IP address, port number, and network protocol.
|
||||||
|
Endpoint local_endpoint = 6;
|
||||||
|
|
||||||
|
// (Optional) Endpoint information of the remote server, such as IP address,
|
||||||
|
// port number, and network protocol.
|
||||||
|
Endpoint remote_endpoint = 7;
|
||||||
|
|
||||||
|
// (Optional) If target name is provided, a secure naming check is performed
|
||||||
|
// to verify that the peer authenticated identity is indeed authorized to run
|
||||||
|
// the target name.
|
||||||
|
string target_name = 8;
|
||||||
|
|
||||||
|
// (Optional) RPC protocol versions supported by the client.
|
||||||
|
RpcProtocolVersions rpc_versions = 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ServerHandshakeParameters {
|
||||||
|
// The record protocols supported by the server, e.g.,
|
||||||
|
// "ALTSRP_GCM_AES128".
|
||||||
|
repeated string record_protocols = 1;
|
||||||
|
|
||||||
|
// (Optional) A list of local identities supported by the server, if
|
||||||
|
// specified. Otherwise, the handshaker chooses a default local identity.
|
||||||
|
repeated Identity local_identities = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message StartServerHandshakeReq {
|
||||||
|
// The application protocols supported by the server, e.g., "h2" (for http2),
|
||||||
|
// "grpc".
|
||||||
|
repeated string application_protocols = 1;
|
||||||
|
|
||||||
|
// Handshake parameters (record protocols and local identities supported by
|
||||||
|
// the server) mapped by the handshake protocol. Each handshake security
|
||||||
|
// protocol (e.g., TLS or ALTS) has its own set of record protocols and local
|
||||||
|
// identities. Since protobuf does not support enum as key to the map, the key
|
||||||
|
// to handshake_parameters is the integer value of HandshakeProtocol enum.
|
||||||
|
map<int32, ServerHandshakeParameters> handshake_parameters = 2;
|
||||||
|
|
||||||
|
// Bytes in out_frames returned from the peer's HandshakerResp. It is possible
|
||||||
|
// that the peer's out_frames are split into multiple HandshakReq messages.
|
||||||
|
bytes in_bytes = 3;
|
||||||
|
|
||||||
|
// (Optional) Local endpoint information of the connection to the client,
|
||||||
|
// such as local IP address, port number, and network protocol.
|
||||||
|
Endpoint local_endpoint = 4;
|
||||||
|
|
||||||
|
// (Optional) Endpoint information of the remote client, such as IP address,
|
||||||
|
// port number, and network protocol.
|
||||||
|
Endpoint remote_endpoint = 5;
|
||||||
|
|
||||||
|
// (Optional) RPC protocol versions supported by the server.
|
||||||
|
RpcProtocolVersions rpc_versions = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message NextHandshakeMessageReq {
|
||||||
|
// Bytes in out_frames returned from the peer's HandshakerResp. It is possible
|
||||||
|
// that the peer's out_frames are split into multiple NextHandshakerMessageReq
|
||||||
|
// messages.
|
||||||
|
bytes in_bytes = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HandshakerReq {
|
||||||
|
oneof req_oneof {
|
||||||
|
// The start client handshake request message.
|
||||||
|
StartClientHandshakeReq client_start = 1;
|
||||||
|
|
||||||
|
// The start server handshake request message.
|
||||||
|
StartServerHandshakeReq server_start = 2;
|
||||||
|
|
||||||
|
// The next handshake request message.
|
||||||
|
NextHandshakeMessageReq next = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
message HandshakerResult {
|
||||||
|
// The application protocol negotiated for this connection.
|
||||||
|
string application_protocol = 1;
|
||||||
|
|
||||||
|
// The record protocol negotiated for this connection.
|
||||||
|
string record_protocol = 2;
|
||||||
|
|
||||||
|
// Cryptographic key data. The key data may be more than the key length
|
||||||
|
// required for the record protocol, thus the client of the handshaker
|
||||||
|
// service needs to truncate the key data into the right key length.
|
||||||
|
bytes key_data = 3;
|
||||||
|
|
||||||
|
// The authenticated identity of the peer.
|
||||||
|
Identity peer_identity = 4;
|
||||||
|
|
||||||
|
// The local identity used in the handshake.
|
||||||
|
Identity local_identity = 5;
|
||||||
|
|
||||||
|
// Indicate whether the handshaker service client should keep the channel
|
||||||
|
// between the handshaker service open, e.g., in order to handle
|
||||||
|
// post-handshake messages in the future.
|
||||||
|
bool keep_channel_open = 6;
|
||||||
|
|
||||||
|
// The RPC protocol versions supported by the peer.
|
||||||
|
RpcProtocolVersions peer_rpc_versions = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HandshakerStatus {
|
||||||
|
// The status code. This could be the gRPC status code.
|
||||||
|
uint32 code = 1;
|
||||||
|
|
||||||
|
// The status details.
|
||||||
|
string details = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message HandshakerResp {
|
||||||
|
// Frames to be given to the peer for the NextHandshakeMessageReq. May be
|
||||||
|
// empty if no out_frames have to be sent to the peer or if in_bytes in the
|
||||||
|
// HandshakerReq are incomplete. All the non-empty out frames must be sent to
|
||||||
|
// the peer even if the handshaker status is not OK as these frames may
|
||||||
|
// contain the alert frames.
|
||||||
|
bytes out_frames = 1;
|
||||||
|
|
||||||
|
// Number of bytes in the in_bytes consumed by the handshaker. It is possible
|
||||||
|
// that part of in_bytes in HandshakerReq was unrelated to the handshake
|
||||||
|
// process.
|
||||||
|
uint32 bytes_consumed = 2;
|
||||||
|
|
||||||
|
// This is set iff the handshake was successful. out_frames may still be set
|
||||||
|
// to frames that needs to be forwarded to the peer.
|
||||||
|
HandshakerResult result = 3;
|
||||||
|
|
||||||
|
// Status of the handshaker.
|
||||||
|
HandshakerStatus status = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
service HandshakerService {
|
||||||
|
// Accepts a stream of handshaker request, returning a stream of handshaker
|
||||||
|
// response.
|
||||||
|
rpc DoHandshake(stream HandshakerReq)
|
||||||
|
returns (stream HandshakerResp) {
|
||||||
|
}
|
||||||
|
}
|
120
credentials/alts/core/proto/transport_security_common.pb.go
Normal file
120
credentials/alts/core/proto/transport_security_common.pb.go
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||||
|
// source: transport_security_common.proto
|
||||||
|
|
||||||
|
package grpc_gcp
|
||||||
|
|
||||||
|
import proto "github.com/golang/protobuf/proto"
|
||||||
|
import fmt "fmt"
|
||||||
|
import math "math"
|
||||||
|
|
||||||
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
|
var _ = proto.Marshal
|
||||||
|
var _ = fmt.Errorf
|
||||||
|
var _ = math.Inf
|
||||||
|
|
||||||
|
// The security level of the created channel. The list is sorted in increasing
|
||||||
|
// level of security. This order must always be maintained.
|
||||||
|
type SecurityLevel int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
SecurityLevel_SECURITY_NONE SecurityLevel = 0
|
||||||
|
SecurityLevel_INTEGRITY_ONLY SecurityLevel = 1
|
||||||
|
SecurityLevel_INTEGRITY_AND_PRIVACY SecurityLevel = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
var SecurityLevel_name = map[int32]string{
|
||||||
|
0: "SECURITY_NONE",
|
||||||
|
1: "INTEGRITY_ONLY",
|
||||||
|
2: "INTEGRITY_AND_PRIVACY",
|
||||||
|
}
|
||||||
|
var SecurityLevel_value = map[string]int32{
|
||||||
|
"SECURITY_NONE": 0,
|
||||||
|
"INTEGRITY_ONLY": 1,
|
||||||
|
"INTEGRITY_AND_PRIVACY": 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x SecurityLevel) String() string {
|
||||||
|
return proto.EnumName(SecurityLevel_name, int32(x))
|
||||||
|
}
|
||||||
|
func (SecurityLevel) EnumDescriptor() ([]byte, []int) { return fileDescriptor2, []int{0} }
|
||||||
|
|
||||||
|
// Max and min supported RPC protocol versions.
|
||||||
|
type RpcProtocolVersions struct {
|
||||||
|
// Maximum supported RPC version.
|
||||||
|
MaxRpcVersion *RpcProtocolVersions_Version `protobuf:"bytes,1,opt,name=max_rpc_version,json=maxRpcVersion" json:"max_rpc_version,omitempty"`
|
||||||
|
// Minimum supported RPC version.
|
||||||
|
MinRpcVersion *RpcProtocolVersions_Version `protobuf:"bytes,2,opt,name=min_rpc_version,json=minRpcVersion" json:"min_rpc_version,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RpcProtocolVersions) Reset() { *m = RpcProtocolVersions{} }
|
||||||
|
func (m *RpcProtocolVersions) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*RpcProtocolVersions) ProtoMessage() {}
|
||||||
|
func (*RpcProtocolVersions) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{0} }
|
||||||
|
|
||||||
|
func (m *RpcProtocolVersions) GetMaxRpcVersion() *RpcProtocolVersions_Version {
|
||||||
|
if m != nil {
|
||||||
|
return m.MaxRpcVersion
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RpcProtocolVersions) GetMinRpcVersion() *RpcProtocolVersions_Version {
|
||||||
|
if m != nil {
|
||||||
|
return m.MinRpcVersion
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPC version contains a major version and a minor version.
|
||||||
|
type RpcProtocolVersions_Version struct {
|
||||||
|
Major uint32 `protobuf:"varint,1,opt,name=major" json:"major,omitempty"`
|
||||||
|
Minor uint32 `protobuf:"varint,2,opt,name=minor" json:"minor,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RpcProtocolVersions_Version) Reset() { *m = RpcProtocolVersions_Version{} }
|
||||||
|
func (m *RpcProtocolVersions_Version) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*RpcProtocolVersions_Version) ProtoMessage() {}
|
||||||
|
func (*RpcProtocolVersions_Version) Descriptor() ([]byte, []int) { return fileDescriptor2, []int{0, 0} }
|
||||||
|
|
||||||
|
func (m *RpcProtocolVersions_Version) GetMajor() uint32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Major
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RpcProtocolVersions_Version) GetMinor() uint32 {
|
||||||
|
if m != nil {
|
||||||
|
return m.Minor
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
proto.RegisterType((*RpcProtocolVersions)(nil), "grpc.gcp.RpcProtocolVersions")
|
||||||
|
proto.RegisterType((*RpcProtocolVersions_Version)(nil), "grpc.gcp.RpcProtocolVersions.Version")
|
||||||
|
proto.RegisterEnum("grpc.gcp.SecurityLevel", SecurityLevel_name, SecurityLevel_value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() { proto.RegisterFile("transport_security_common.proto", fileDescriptor2) }
|
||||||
|
|
||||||
|
var fileDescriptor2 = []byte{
|
||||||
|
// 261 bytes of a gzipped FileDescriptorProto
|
||||||
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2f, 0x29, 0x4a, 0xcc,
|
||||||
|
0x2b, 0x2e, 0xc8, 0x2f, 0x2a, 0x89, 0x2f, 0x4e, 0x4d, 0x2e, 0x2d, 0xca, 0x2c, 0xa9, 0x8c, 0x4f,
|
||||||
|
0xce, 0xcf, 0xcd, 0xcd, 0xcf, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x48, 0x2f, 0x2a,
|
||||||
|
0x48, 0xd6, 0x4b, 0x4f, 0x2e, 0x50, 0x7a, 0xc5, 0xc8, 0x25, 0x1c, 0x54, 0x90, 0x1c, 0x00, 0x12,
|
||||||
|
0x4e, 0xce, 0xcf, 0x09, 0x4b, 0x2d, 0x2a, 0xce, 0xcc, 0xcf, 0x2b, 0x16, 0xf2, 0xe5, 0xe2, 0xcf,
|
||||||
|
0x4d, 0xac, 0x88, 0x2f, 0x2a, 0x48, 0x8e, 0x2f, 0x83, 0x88, 0x49, 0x30, 0x2a, 0x30, 0x6a, 0x70,
|
||||||
|
0x1b, 0xa9, 0xea, 0xc1, 0xf4, 0xea, 0x61, 0xd1, 0xa7, 0x07, 0x65, 0x04, 0xf1, 0xe6, 0x26, 0x56,
|
||||||
|
0x04, 0x15, 0x24, 0x43, 0xb9, 0x60, 0xe3, 0x32, 0xf3, 0x50, 0x8c, 0x63, 0x22, 0xcd, 0xb8, 0xcc,
|
||||||
|
0x3c, 0x84, 0x71, 0x52, 0xa6, 0x5c, 0xec, 0x30, 0x93, 0x45, 0xb8, 0x58, 0x73, 0x13, 0xb3, 0xf2,
|
||||||
|
0x8b, 0xc0, 0xce, 0xe3, 0x0d, 0x82, 0x70, 0xc0, 0xa2, 0x99, 0x79, 0xf9, 0x45, 0x60, 0x5b, 0x40,
|
||||||
|
0xa2, 0x20, 0x8e, 0x56, 0x20, 0x17, 0x6f, 0x30, 0x34, 0x3c, 0x7c, 0x52, 0xcb, 0x52, 0x73, 0x84,
|
||||||
|
0x04, 0xb9, 0x78, 0x83, 0x5d, 0x9d, 0x43, 0x83, 0x3c, 0x43, 0x22, 0xe3, 0xfd, 0xfc, 0xfd, 0x5c,
|
||||||
|
0x05, 0x18, 0x84, 0x84, 0xb8, 0xf8, 0x3c, 0xfd, 0x42, 0x5c, 0xdd, 0xc1, 0x62, 0xfe, 0x7e, 0x3e,
|
||||||
|
0x91, 0x02, 0x8c, 0x42, 0x92, 0x5c, 0xa2, 0x08, 0x31, 0x47, 0x3f, 0x97, 0xf8, 0x80, 0x20, 0xcf,
|
||||||
|
0x30, 0x47, 0xe7, 0x48, 0x01, 0x26, 0x27, 0x3e, 0x2e, 0x9e, 0xcc, 0x7c, 0x88, 0x1f, 0x12, 0x73,
|
||||||
|
0x4a, 0x8a, 0x93, 0xd8, 0xc0, 0x01, 0x6c, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x11, 0x06, 0x14,
|
||||||
|
0x7a, 0x83, 0x01, 0x00, 0x00,
|
||||||
|
}
|
40
credentials/alts/core/proto/transport_security_common.proto
Normal file
40
credentials/alts/core/proto/transport_security_common.proto
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Copyright 2018 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.
|
||||||
|
|
||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package grpc.gcp;
|
||||||
|
|
||||||
|
option java_package = "io.grpc.alts";
|
||||||
|
|
||||||
|
// The security level of the created channel. The list is sorted in increasing
|
||||||
|
// level of security. This order must always be maintained.
|
||||||
|
enum SecurityLevel {
|
||||||
|
SECURITY_NONE = 0;
|
||||||
|
INTEGRITY_ONLY = 1;
|
||||||
|
INTEGRITY_AND_PRIVACY = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max and min supported RPC protocol versions.
|
||||||
|
message RpcProtocolVersions {
|
||||||
|
// RPC version contains a major version and a minor version.
|
||||||
|
message Version {
|
||||||
|
uint32 major = 1;
|
||||||
|
uint32 minor = 2;
|
||||||
|
}
|
||||||
|
// Maximum supported RPC version.
|
||||||
|
Version max_rpc_version = 1;
|
||||||
|
// Minimum supported RPC version.
|
||||||
|
Version min_rpc_version = 2;
|
||||||
|
}
|
125
credentials/alts/core/testutil/testutil.go
Normal file
125
credentials/alts/core/testutil/testutil.go
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Package testutil include useful test utilities for the handshaker.
|
||||||
|
package testutil
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/credentials/alts/core/conn"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Stats is used to collect statistics about concurrent handshake calls.
|
||||||
|
type Stats struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
calls int
|
||||||
|
MaxConcurrentCalls int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update updates the statistics by adding one call.
|
||||||
|
func (s *Stats) Update() func() {
|
||||||
|
s.mu.Lock()
|
||||||
|
s.calls++
|
||||||
|
if s.calls > s.MaxConcurrentCalls {
|
||||||
|
s.MaxConcurrentCalls = s.calls
|
||||||
|
}
|
||||||
|
s.mu.Unlock()
|
||||||
|
|
||||||
|
return func() {
|
||||||
|
s.mu.Lock()
|
||||||
|
s.calls--
|
||||||
|
s.mu.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset resets the statistics.
|
||||||
|
func (s *Stats) Reset() {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
|
s.calls = 0
|
||||||
|
s.MaxConcurrentCalls = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// testConn mimics a net.Conn to the peer.
|
||||||
|
type testConn struct {
|
||||||
|
net.Conn
|
||||||
|
in *bytes.Buffer
|
||||||
|
out *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTestConn creates a new instance of testConn object.
|
||||||
|
func NewTestConn(in *bytes.Buffer, out *bytes.Buffer) net.Conn {
|
||||||
|
return &testConn{
|
||||||
|
in: in,
|
||||||
|
out: out,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads from the in buffer.
|
||||||
|
func (c *testConn) Read(b []byte) (n int, err error) {
|
||||||
|
return c.in.Read(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes to the out buffer.
|
||||||
|
func (c *testConn) Write(b []byte) (n int, err error) {
|
||||||
|
return c.out.Write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the testConn object.
|
||||||
|
func (c *testConn) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// unresponsiveTestConn mimics a net.Conn for an unresponsive peer. It is used
|
||||||
|
// for testing the PeerNotResponding case.
|
||||||
|
type unresponsiveTestConn struct {
|
||||||
|
net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewUnresponsiveTestConn creates a new instance of unresponsiveTestConn object.
|
||||||
|
func NewUnresponsiveTestConn() net.Conn {
|
||||||
|
return &unresponsiveTestConn{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read reads from the in buffer.
|
||||||
|
func (c *unresponsiveTestConn) Read(b []byte) (n int, err error) {
|
||||||
|
return 0, io.EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write writes to the out buffer.
|
||||||
|
func (c *unresponsiveTestConn) Write(b []byte) (n int, err error) {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the TestConn object.
|
||||||
|
func (c *unresponsiveTestConn) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MakeFrame creates a handshake frame.
|
||||||
|
func MakeFrame(pl string) []byte {
|
||||||
|
f := make([]byte, len(pl)+conn.MsgLenFieldSize)
|
||||||
|
binary.LittleEndian.PutUint32(f, uint32(len(pl)))
|
||||||
|
copy(f[conn.MsgLenFieldSize:], []byte(pl))
|
||||||
|
return f
|
||||||
|
}
|
117
credentials/alts/utils.go
Normal file
117
credentials/alts/utils.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package alts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
linuxProductNameFile = "/sys/class/dmi/id/product_name"
|
||||||
|
windowsCheckCommand = "powershell.exe"
|
||||||
|
windowsCheckCommandArgs = "Get-WmiObject -Class Win32_BIOS"
|
||||||
|
powershellOutputFilter = "Manufacturer"
|
||||||
|
windowsManufacturerRegex = ":(.*)"
|
||||||
|
windowsCheckTimeout = 30 * time.Second
|
||||||
|
)
|
||||||
|
|
||||||
|
type platformError string
|
||||||
|
|
||||||
|
func (k platformError) Error() string {
|
||||||
|
return fmt.Sprintf("%v is not supported", k)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// The following two variables will be reassigned in tests.
|
||||||
|
runningOS = runtime.GOOS
|
||||||
|
manufacturerReader = func() (io.Reader, error) {
|
||||||
|
switch runningOS {
|
||||||
|
case "linux":
|
||||||
|
return os.Open(linuxProductNameFile)
|
||||||
|
case "windows":
|
||||||
|
cmd := exec.Command(windowsCheckCommand, windowsCheckCommandArgs)
|
||||||
|
out, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, line := range strings.Split(strings.TrimSuffix(string(out), "\n"), "\n") {
|
||||||
|
if strings.HasPrefix(line, powershellOutputFilter) {
|
||||||
|
re := regexp.MustCompile(windowsManufacturerRegex)
|
||||||
|
name := re.FindString(line)
|
||||||
|
name = strings.TrimLeft(name, ":")
|
||||||
|
return strings.NewReader(name), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("cannot determine the machine's manufacturer")
|
||||||
|
default:
|
||||||
|
return nil, platformError(runningOS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vmOnGCP bool
|
||||||
|
)
|
||||||
|
|
||||||
|
// isRunningOnGCP checks whether the local system, without doing a network request is
|
||||||
|
// running on GCP.
|
||||||
|
func isRunningOnGCP() bool {
|
||||||
|
manufacturer, err := readManufacturer()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failure to read manufacturer information: %v", err)
|
||||||
|
}
|
||||||
|
name := string(manufacturer)
|
||||||
|
switch runningOS {
|
||||||
|
case "linux":
|
||||||
|
name = strings.TrimSpace(name)
|
||||||
|
return name == "Google" || name == "Google Compute Engine"
|
||||||
|
case "windows":
|
||||||
|
name = strings.Replace(name, " ", "", -1)
|
||||||
|
name = strings.Replace(name, "\n", "", -1)
|
||||||
|
name = strings.Replace(name, "\r", "", -1)
|
||||||
|
return name == "Google"
|
||||||
|
default:
|
||||||
|
log.Fatal(platformError(runningOS))
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func readManufacturer() ([]byte, error) {
|
||||||
|
reader, err := manufacturerReader()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if reader == nil {
|
||||||
|
return nil, errors.New("got nil reader")
|
||||||
|
}
|
||||||
|
manufacturer, err := ioutil.ReadAll(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed reading %v: %v", linuxProductNameFile, err)
|
||||||
|
}
|
||||||
|
return manufacturer, nil
|
||||||
|
}
|
66
credentials/alts/utils_test.go
Normal file
66
credentials/alts/utils_test.go
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package alts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIsRunningOnGCP(t *testing.T) {
|
||||||
|
for _, tc := range []struct {
|
||||||
|
description string
|
||||||
|
testOS string
|
||||||
|
testReader io.Reader
|
||||||
|
out bool
|
||||||
|
}{
|
||||||
|
// Linux tests.
|
||||||
|
{"linux: not a GCP platform", "linux", strings.NewReader("not GCP"), false},
|
||||||
|
{"Linux: GCP platform (Google)", "linux", strings.NewReader("Google"), true},
|
||||||
|
{"Linux: GCP platform (Google Compute Engine)", "linux", strings.NewReader("Google Compute Engine"), true},
|
||||||
|
{"Linux: GCP platform (Google Compute Engine) with extra spaces", "linux", strings.NewReader(" Google Compute Engine "), true},
|
||||||
|
// Windows tests.
|
||||||
|
{"windows: not a GCP platform", "windows", strings.NewReader("not GCP"), false},
|
||||||
|
{"windows: GCP platform (Google)", "windows", strings.NewReader("Google"), true},
|
||||||
|
{"windows: GCP platform (Google) with extra spaces", "windows", strings.NewReader(" Google "), true},
|
||||||
|
} {
|
||||||
|
reverseFunc := setup(tc.testOS, tc.testReader)
|
||||||
|
if got, want := isRunningOnGCP(), tc.out; got != want {
|
||||||
|
t.Errorf("%v: isRunningOnGCP()=%v, want %v", tc.description, got, want)
|
||||||
|
}
|
||||||
|
reverseFunc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setup(testOS string, testReader io.Reader) func() {
|
||||||
|
tmpOS := runningOS
|
||||||
|
tmpReader := manufacturerReader
|
||||||
|
|
||||||
|
// Set test OS and reader function.
|
||||||
|
runningOS = testOS
|
||||||
|
manufacturerReader = func() (io.Reader, error) {
|
||||||
|
return testReader, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return func() {
|
||||||
|
runningOS = tmpOS
|
||||||
|
manufacturerReader = tmpReader
|
||||||
|
}
|
||||||
|
}
|
65
interop/alts/client/client.go
Normal file
65
interop/alts/client/client.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This binary can only run on Google Cloud Platform (GCP).
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials/alts"
|
||||||
|
"google.golang.org/grpc/grpclog"
|
||||||
|
testpb "google.golang.org/grpc/interop/grpc_testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
value = "test_value"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
serverAddr = flag.String("server_address", ":8080", "The port on which the server is listening")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
altsTC := alts.NewClientALTS(nil)
|
||||||
|
// Block until the server is ready.
|
||||||
|
conn, err := grpc.Dial(*serverAddr, grpc.WithTransportCredentials(altsTC), grpc.WithBlock())
|
||||||
|
if err != nil {
|
||||||
|
grpclog.Fatalf("gRPC Client: failed to dial the server at %v: %v", *serverAddr, err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
grpcClient := testpb.NewTestServiceClient(conn)
|
||||||
|
|
||||||
|
// Call the EmptyCall API.
|
||||||
|
ctx := context.Background()
|
||||||
|
request := &testpb.Empty{}
|
||||||
|
if _, err := grpcClient.EmptyCall(ctx, request); err != nil {
|
||||||
|
grpclog.Fatalf("grpc Client: EmptyCall(_, %v) failed: %v", request, err)
|
||||||
|
}
|
||||||
|
grpclog.Info("grpc Client: empty call succeeded")
|
||||||
|
|
||||||
|
// This sleep prevents the connection from being abruptly disconnected
|
||||||
|
// when running this binary (along with grpc_server) on GCP dev cluster.
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
49
interop/alts/server/server.go
Normal file
49
interop/alts/server/server.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This binary can only run on Google Cloud Platform (GCP).
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
grpc "google.golang.org/grpc"
|
||||||
|
"google.golang.org/grpc/credentials/alts"
|
||||||
|
"google.golang.org/grpc/grpclog"
|
||||||
|
"google.golang.org/grpc/interop"
|
||||||
|
testpb "google.golang.org/grpc/interop/grpc_testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
serverAddr = flag.String("server_address", ":8080", "The port on which the server is listening")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
lis, err := net.Listen("tcp", *serverAddr)
|
||||||
|
if err != nil {
|
||||||
|
grpclog.Fatalf("gRPC Server: failed to start the server at %v: %v", *serverAddr, err)
|
||||||
|
}
|
||||||
|
altsTC := alts.NewServerALTS()
|
||||||
|
grpcServer := grpc.NewServer(grpc.Creds(altsTC))
|
||||||
|
testpb.RegisterTestServiceServer(grpcServer, interop.NewTestServer())
|
||||||
|
grpcServer.Serve(lis)
|
||||||
|
}
|
Reference in New Issue
Block a user