update vendor

Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
This commit is contained in:
Jess Frazelle 2018-09-25 12:27:46 -04:00
parent 19a32db84d
commit 94d1cfbfbf
No known key found for this signature in database
GPG key ID: 18F3685C0022BFF3
10501 changed files with 2307943 additions and 29279 deletions

329
vendor/google.golang.org/grpc/credentials/alts/alts.go generated vendored Normal file
View file

@ -0,0 +1,329 @@
/*
*
* 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"
"fmt"
"net"
"sync"
"time"
"golang.org/x/net/context"
"google.golang.org/grpc/credentials"
core "google.golang.org/grpc/credentials/alts/internal"
"google.golang.org/grpc/credentials/alts/internal/handshaker"
"google.golang.org/grpc/credentials/alts/internal/handshaker/service"
altspb "google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp"
"google.golang.org/grpc/grpclog"
)
const (
// hypervisorHandshakerServiceAddress represents the default ALTS gRPC
// handshaker service address in the hypervisor.
hypervisorHandshakerServiceAddress = "metadata.google.internal:8080"
// 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 (
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")
)
// 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
}
// ClientOptions contains the client-side options of an ALTS channel. These
// options will be passed to the underlying ALTS handshaker.
type ClientOptions struct {
// TargetServiceAccounts contains a list of expected target service
// accounts.
TargetServiceAccounts []string
// HandshakerServiceAddress represents the ALTS handshaker gRPC service
// address to connect to.
HandshakerServiceAddress string
}
// DefaultClientOptions creates a new ClientOptions object with the default
// values.
func DefaultClientOptions() *ClientOptions {
return &ClientOptions{
HandshakerServiceAddress: hypervisorHandshakerServiceAddress,
}
}
// ServerOptions contains the server-side options of an ALTS channel. These
// options will be passed to the underlying ALTS handshaker.
type ServerOptions struct {
// HandshakerServiceAddress represents the ALTS handshaker gRPC service
// address to connect to.
HandshakerServiceAddress string
}
// DefaultServerOptions creates a new ServerOptions object with the default
// values.
func DefaultServerOptions() *ServerOptions {
return &ServerOptions{
HandshakerServiceAddress: hypervisorHandshakerServiceAddress,
}
}
// altsTC is the credentials required for authenticating a connection using ALTS.
// It implements credentials.TransportCredentials interface.
type altsTC struct {
info *credentials.ProtocolInfo
side core.Side
accounts []string
hsAddress string
}
// NewClientCreds constructs a client-side ALTS TransportCredentials object.
func NewClientCreds(opts *ClientOptions) credentials.TransportCredentials {
return newALTS(core.ClientSide, opts.TargetServiceAccounts, opts.HandshakerServiceAddress)
}
// NewServerCreds constructs a server-side ALTS TransportCredentials object.
func NewServerCreds(opts *ServerOptions) credentials.TransportCredentials {
return newALTS(core.ServerSide, nil, opts.HandshakerServiceAddress)
}
func newALTS(side core.Side, accounts []string, hsAddress string) credentials.TransportCredentials {
once.Do(func() {
vmOnGCP = isRunningOnGCP()
})
if hsAddress == "" {
hsAddress = hypervisorHandshakerServiceAddress
}
return &altsTC{
info: &credentials.ProtocolInfo{
SecurityProtocol: "alts",
SecurityVersion: "1.0",
},
side: side,
accounts: accounts,
hsAddress: hsAddress,
}
}
// 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 !vmOnGCP {
return nil, nil, ErrUntrustedPlatform
}
// Connecting to ALTS handshaker service.
hsConn, err := service.Dial(g.hsAddress)
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 !vmOnGCP {
return nil, nil, ErrUntrustedPlatform
}
// Connecting to ALTS handshaker service.
hsConn, err := service.Dial(g.hsAddress)
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
var accounts []string
if g.accounts != nil {
accounts = make([]string, len(g.accounts))
copy(accounts, g.accounts)
}
return &altsTC{
info: &info,
side: g.side,
hsAddress: g.hsAddress,
accounts: accounts,
}
}
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
}

View file

@ -0,0 +1,290 @@
/*
*
* 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 (
"reflect"
"testing"
"github.com/golang/protobuf/proto"
altspb "google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp"
)
func TestInfoServerName(t *testing.T) {
// This is not testing any handshaker functionality, so it's fine to only
// use NewServerCreds and not NewClientCreds.
alts := NewServerCreds(DefaultServerOptions())
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 NewServerCreds and not NewClientCreds.
c := NewServerCreds(DefaultServerOptions())
c.OverrideServerName(wantServerName)
if got, want := c.Info().ServerName, wantServerName; got != want {
t.Fatalf("c.Info().ServerName = %v, want %v", got, want)
}
}
func TestCloneClient(t *testing.T) {
wantServerName := "server.name"
opt := DefaultClientOptions()
opt.TargetServiceAccounts = []string{"not", "empty"}
c := NewClientCreds(opt)
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)
}
ct := c.(*altsTC)
cct := cc.(*altsTC)
if ct.side != cct.side {
t.Errorf("cc.side = %q, want %q", cct.side, ct.side)
}
if ct.hsAddress != cct.hsAddress {
t.Errorf("cc.hsAddress = %q, want %q", cct.hsAddress, ct.hsAddress)
}
if !reflect.DeepEqual(ct.accounts, cct.accounts) {
t.Errorf("cc.accounts = %q, want %q", cct.accounts, ct.accounts)
}
}
func TestCloneServer(t *testing.T) {
wantServerName := "server.name"
c := NewServerCreds(DefaultServerOptions())
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)
}
ct := c.(*altsTC)
cct := cc.(*altsTC)
if ct.side != cct.side {
t.Errorf("cc.side = %q, want %q", cct.side, ct.side)
}
if ct.hsAddress != cct.hsAddress {
t.Errorf("cc.hsAddress = %q, want %q", cct.hsAddress, ct.hsAddress)
}
if !reflect.DeepEqual(ct.accounts, cct.accounts) {
t.Errorf("cc.accounts = %q, want %q", cct.accounts, ct.accounts)
}
}
func TestInfo(t *testing.T) {
// This is not testing any handshaker functionality, so it's fine to only
// use NewServerCreds and not NewClientCreds.
c := NewServerCreds(DefaultServerOptions())
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),
}
}

View 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/internal/proto/grpc_gcp"
)
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()
}

View 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/internal/proto/grpc_gcp"
)
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)
}
}
}

View file

@ -0,0 +1,69 @@
/*
*
* 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.
*
*/
//go:generate ./regenerate.sh
// Package internal contains common core functionality for ALTS.
package internal
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()
}

View 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]
}

View 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
}

View 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"
core "google.golang.org/grpc/credentials/alts/internal"
)
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
}

View 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"
core "google.golang.org/grpc/credentials/alts/internal"
)
// 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)
}

View 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"
core "google.golang.org/grpc/credentials/alts/internal"
)
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
}

View 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"
core "google.golang.org/grpc/credentials/alts/internal"
)
// 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)
}

View 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
}

View file

@ -0,0 +1,62 @@
/*
*
* 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"
)
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
}
// 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
}
}

View 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"
core "google.golang.org/grpc/credentials/alts/internal"
)
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)
}
}
}

View 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"
core "google.golang.org/grpc/credentials/alts/internal"
)
// 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
}

View 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"
core "google.golang.org/grpc/credentials/alts/internal"
)
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)
}
}

View file

@ -0,0 +1,63 @@
/*
*
* 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 core "google.golang.org/grpc/credentials/alts/internal"
// 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
}
// 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
}

View file

@ -0,0 +1,365 @@
/*
*
* 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"
core "google.golang.org/grpc/credentials/alts/internal"
"google.golang.org/grpc/credentials/alts/internal/authinfo"
"google.golang.org/grpc/credentials/alts/internal/conn"
altsgrpc "google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp"
altspb "google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp"
)
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 altsgrpc.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 := altsgrpc.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 := altsgrpc.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()
}

View 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"
core "google.golang.org/grpc/credentials/alts/internal"
altspb "google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp"
"google.golang.org/grpc/credentials/alts/internal/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.
cleanup := stat.Update()
defer cleanup()
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)
}
}

View file

@ -0,0 +1,56 @@
/*
*
* 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 (
"sync"
grpc "google.golang.org/grpc"
)
var (
// 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(hsAddress string) (*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(hsAddress, grpc.WithInsecure())
if err != nil {
return nil, err
}
}
return hsConn, nil
}

View file

@ -0,0 +1,69 @@
/*
*
* 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"
)
const (
// The address is irrelevant in this test.
testAddress = "some_address"
)
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(testAddress)
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(testAddress)
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)
}
}

View file

@ -0,0 +1,136 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: grpc/gcp/altscontext.proto
package grpc_gcp // import "google.golang.org/grpc/credentials/alts/internal/proto/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,proto3" json:"application_protocol,omitempty"`
// The record protocol negotiated for this connection.
RecordProtocol string `protobuf:"bytes,2,opt,name=record_protocol,json=recordProtocol,proto3" json:"record_protocol,omitempty"`
// The security level of the created secure channel.
SecurityLevel SecurityLevel `protobuf:"varint,3,opt,name=security_level,json=securityLevel,proto3,enum=grpc.gcp.SecurityLevel" json:"security_level,omitempty"`
// The peer service account.
PeerServiceAccount string `protobuf:"bytes,4,opt,name=peer_service_account,json=peerServiceAccount,proto3" json:"peer_service_account,omitempty"`
// The local service account.
LocalServiceAccount string `protobuf:"bytes,5,opt,name=local_service_account,json=localServiceAccount,proto3" 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,proto3" json:"peer_rpc_versions,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AltsContext) Reset() { *m = AltsContext{} }
func (m *AltsContext) String() string { return proto.CompactTextString(m) }
func (*AltsContext) ProtoMessage() {}
func (*AltsContext) Descriptor() ([]byte, []int) {
return fileDescriptor_altscontext_4d8d0120fd718d8c, []int{0}
}
func (m *AltsContext) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AltsContext.Unmarshal(m, b)
}
func (m *AltsContext) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AltsContext.Marshal(b, m, deterministic)
}
func (dst *AltsContext) XXX_Merge(src proto.Message) {
xxx_messageInfo_AltsContext.Merge(dst, src)
}
func (m *AltsContext) XXX_Size() int {
return xxx_messageInfo_AltsContext.Size(m)
}
func (m *AltsContext) XXX_DiscardUnknown() {
xxx_messageInfo_AltsContext.DiscardUnknown(m)
}
var xxx_messageInfo_AltsContext proto.InternalMessageInfo
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("grpc/gcp/altscontext.proto", fileDescriptor_altscontext_4d8d0120fd718d8c)
}
var fileDescriptor_altscontext_4d8d0120fd718d8c = []byte{
// 335 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x91, 0xcf, 0x4b, 0x2b, 0x31,
0x10, 0xc7, 0xd9, 0xbe, 0xf7, 0xca, 0x33, 0xc5, 0x56, 0xd7, 0x16, 0x97, 0x82, 0x50, 0xbc, 0xb8,
0xa7, 0x5d, 0xad, 0x77, 0xa5, 0xf5, 0x24, 0x78, 0x28, 0x5b, 0xf0, 0xe0, 0x65, 0x89, 0xd3, 0x10,
0x02, 0x69, 0x26, 0x4c, 0xd2, 0xa2, 0xff, 0xaa, 0x7f, 0x8d, 0x6c, 0xd2, 0x6d, 0x8b, 0x1e, 0x67,
0x3e, 0xdf, 0xef, 0xfc, 0x64, 0x63, 0x49, 0x16, 0x4a, 0x09, 0xb6, 0xe4, 0xda, 0x3b, 0x40, 0xe3,
0xc5, 0x87, 0x2f, 0x2c, 0xa1, 0xc7, 0xf4, 0x7f, 0xc3, 0x0a, 0x09, 0x76, 0x9c, 0xef, 0x55, 0x9e,
0xb8, 0x71, 0x16, 0xc9, 0xd7, 0x4e, 0xc0, 0x86, 0x94, 0xff, 0xac, 0x01, 0xd7, 0x6b, 0x34, 0xd1,
0x73, 0xfd, 0xd5, 0x61, 0xbd, 0x99, 0xf6, 0xee, 0x29, 0x56, 0x4a, 0xef, 0xd8, 0x90, 0x5b, 0xab,
0x15, 0x70, 0xaf, 0xd0, 0xd4, 0x41, 0x04, 0xa8, 0xb3, 0x64, 0x92, 0xe4, 0x27, 0xd5, 0xc5, 0x11,
0x5b, 0xec, 0x50, 0x7a, 0xc3, 0x06, 0x24, 0x00, 0x69, 0x75, 0x50, 0x77, 0x82, 0xba, 0x1f, 0xd3,
0x7b, 0xe1, 0x03, 0xeb, 0xef, 0x87, 0xd0, 0x62, 0x2b, 0x74, 0xf6, 0x67, 0x92, 0xe4, 0xfd, 0xe9,
0x65, 0xd1, 0x0e, 0x5e, 0x2c, 0x77, 0xfc, 0xa5, 0xc1, 0xd5, 0xa9, 0x3b, 0x0e, 0xd3, 0x5b, 0x36,
0xb4, 0x42, 0x50, 0xed, 0x04, 0x6d, 0x15, 0x88, 0x9a, 0x03, 0xe0, 0xc6, 0xf8, 0xec, 0x6f, 0xe8,
0x96, 0x36, 0x6c, 0x19, 0xd1, 0x2c, 0x92, 0x74, 0xca, 0x46, 0x1a, 0x81, 0xeb, 0x5f, 0x96, 0x7f,
0x71, 0x9d, 0x00, 0x7f, 0x78, 0x9e, 0xd9, 0x79, 0xe8, 0x42, 0x16, 0xea, 0xad, 0x20, 0xa7, 0xd0,
0xb8, 0xac, 0x3b, 0x49, 0xf2, 0xde, 0xf4, 0xea, 0x30, 0x68, 0x65, 0xa1, 0xdd, 0xeb, 0x75, 0x27,
0xaa, 0x06, 0x8d, 0xaf, 0xb2, 0xd0, 0x26, 0xe6, 0x9a, 0x8d, 0x14, 0x46, 0x4f, 0xf3, 0xad, 0x42,
0x19, 0x2f, 0xc8, 0x70, 0x3d, 0x3f, 0x3b, 0x3a, 0x79, 0x28, 0xb3, 0x48, 0xde, 0x1e, 0x25, 0xa2,
0xd4, 0xa2, 0x90, 0xa8, 0xb9, 0x91, 0x05, 0x92, 0x2c, 0xc3, 0x17, 0x81, 0xc4, 0x4a, 0x18, 0xaf,
0xb8, 0x76, 0xe1, 0xe7, 0x65, 0x5b, 0xa5, 0x0c, 0xe7, 0x0e, 0xa2, 0x5a, 0x82, 0x7d, 0xef, 0x86,
0xf8, 0xfe, 0x3b, 0x00, 0x00, 0xff, 0xff, 0x15, 0xf8, 0xd5, 0xb6, 0x23, 0x02, 0x00, 0x00,
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,178 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: grpc/gcp/transport_security_common.proto
package grpc_gcp // import "google.golang.org/grpc/credentials/alts/internal/proto/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
// 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 fileDescriptor_transport_security_common_71945991f2c3b4a6, []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,proto3" json:"max_rpc_version,omitempty"`
// Minimum supported RPC version.
MinRpcVersion *RpcProtocolVersions_Version `protobuf:"bytes,2,opt,name=min_rpc_version,json=minRpcVersion,proto3" json:"min_rpc_version,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RpcProtocolVersions) Reset() { *m = RpcProtocolVersions{} }
func (m *RpcProtocolVersions) String() string { return proto.CompactTextString(m) }
func (*RpcProtocolVersions) ProtoMessage() {}
func (*RpcProtocolVersions) Descriptor() ([]byte, []int) {
return fileDescriptor_transport_security_common_71945991f2c3b4a6, []int{0}
}
func (m *RpcProtocolVersions) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RpcProtocolVersions.Unmarshal(m, b)
}
func (m *RpcProtocolVersions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RpcProtocolVersions.Marshal(b, m, deterministic)
}
func (dst *RpcProtocolVersions) XXX_Merge(src proto.Message) {
xxx_messageInfo_RpcProtocolVersions.Merge(dst, src)
}
func (m *RpcProtocolVersions) XXX_Size() int {
return xxx_messageInfo_RpcProtocolVersions.Size(m)
}
func (m *RpcProtocolVersions) XXX_DiscardUnknown() {
xxx_messageInfo_RpcProtocolVersions.DiscardUnknown(m)
}
var xxx_messageInfo_RpcProtocolVersions proto.InternalMessageInfo
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,proto3" json:"major,omitempty"`
Minor uint32 `protobuf:"varint,2,opt,name=minor,proto3" json:"minor,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
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 fileDescriptor_transport_security_common_71945991f2c3b4a6, []int{0, 0}
}
func (m *RpcProtocolVersions_Version) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RpcProtocolVersions_Version.Unmarshal(m, b)
}
func (m *RpcProtocolVersions_Version) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RpcProtocolVersions_Version.Marshal(b, m, deterministic)
}
func (dst *RpcProtocolVersions_Version) XXX_Merge(src proto.Message) {
xxx_messageInfo_RpcProtocolVersions_Version.Merge(dst, src)
}
func (m *RpcProtocolVersions_Version) XXX_Size() int {
return xxx_messageInfo_RpcProtocolVersions_Version.Size(m)
}
func (m *RpcProtocolVersions_Version) XXX_DiscardUnknown() {
xxx_messageInfo_RpcProtocolVersions_Version.DiscardUnknown(m)
}
var xxx_messageInfo_RpcProtocolVersions_Version proto.InternalMessageInfo
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("grpc/gcp/transport_security_common.proto", fileDescriptor_transport_security_common_71945991f2c3b4a6)
}
var fileDescriptor_transport_security_common_71945991f2c3b4a6 = []byte{
// 323 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x91, 0x41, 0x4b, 0x3b, 0x31,
0x10, 0xc5, 0xff, 0x5b, 0xf8, 0xab, 0x44, 0x56, 0xeb, 0x6a, 0x41, 0xc5, 0x83, 0x08, 0x42, 0xf1,
0x90, 0x05, 0xc5, 0xb3, 0xb4, 0xb5, 0x48, 0xa1, 0x6e, 0xeb, 0xb6, 0x16, 0xea, 0x25, 0xc4, 0x18,
0x42, 0x24, 0x9b, 0x09, 0xb3, 0xb1, 0xd4, 0xaf, 0xec, 0xa7, 0x90, 0x4d, 0xbb, 0x14, 0xc1, 0x8b,
0xb7, 0xbc, 0xc7, 0xcc, 0x6f, 0x32, 0xf3, 0x48, 0x5b, 0xa1, 0x13, 0xa9, 0x12, 0x2e, 0xf5, 0xc8,
0x6d, 0xe9, 0x00, 0x3d, 0x2b, 0xa5, 0xf8, 0x40, 0xed, 0x3f, 0x99, 0x80, 0xa2, 0x00, 0x4b, 0x1d,
0x82, 0x87, 0x64, 0xa7, 0xaa, 0xa4, 0x4a, 0xb8, 0x8b, 0xaf, 0x88, 0x1c, 0xe6, 0x4e, 0x8c, 0x2b,
0x5b, 0x80, 0x99, 0x49, 0x2c, 0x35, 0xd8, 0x32, 0x79, 0x24, 0xfb, 0x05, 0x5f, 0x32, 0x74, 0x82,
0x2d, 0x56, 0xde, 0x71, 0x74, 0x1e, 0xb5, 0x77, 0xaf, 0x2f, 0x69, 0xdd, 0x4b, 0x7f, 0xe9, 0xa3,
0xeb, 0x47, 0x1e, 0x17, 0x7c, 0x99, 0x3b, 0xb1, 0x96, 0x01, 0xa7, 0xed, 0x0f, 0x5c, 0xe3, 0x6f,
0x38, 0x6d, 0x37, 0xb8, 0xd3, 0x5b, 0xb2, 0x5d, 0x93, 0x8f, 0xc8, 0xff, 0x82, 0xbf, 0x03, 0x86,
0xef, 0xc5, 0xf9, 0x4a, 0x04, 0x57, 0x5b, 0xc0, 0x30, 0xa5, 0x72, 0x2b, 0x71, 0xf5, 0x44, 0xe2,
0xc9, 0xfa, 0x1e, 0x43, 0xb9, 0x90, 0x26, 0x39, 0x20, 0xf1, 0xa4, 0xdf, 0x7b, 0xce, 0x07, 0xd3,
0x39, 0xcb, 0x46, 0x59, 0xbf, 0xf9, 0x2f, 0x49, 0xc8, 0xde, 0x20, 0x9b, 0xf6, 0x1f, 0x82, 0x37,
0xca, 0x86, 0xf3, 0x66, 0x94, 0x9c, 0x90, 0xd6, 0xc6, 0xeb, 0x64, 0xf7, 0x6c, 0x9c, 0x0f, 0x66,
0x9d, 0xde, 0xbc, 0xd9, 0xe8, 0x2e, 0x49, 0x4b, 0xc3, 0x6a, 0x07, 0x6e, 0x7c, 0x49, 0xb5, 0xf5,
0x12, 0x2d, 0x37, 0xdd, 0xb3, 0x69, 0x9d, 0x41, 0x3d, 0xb2, 0x17, 0x12, 0x08, 0x2b, 0x8e, 0xa3,
0x97, 0x3b, 0x05, 0xa0, 0x8c, 0xa4, 0x0a, 0x0c, 0xb7, 0x8a, 0x02, 0xaa, 0x34, 0xc4, 0x27, 0x50,
0xbe, 0x49, 0xeb, 0x35, 0x37, 0x65, 0x5a, 0x11, 0xd3, 0x9a, 0x98, 0x86, 0xe8, 0x42, 0x11, 0x53,
0xc2, 0xbd, 0x6e, 0x05, 0x7d, 0xf3, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x31, 0x14, 0xb4, 0x11, 0xf6,
0x01, 0x00, 0x00,
}

View file

@ -0,0 +1,35 @@
#!/bin/bash
# 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.
set -eux -o pipefail
TMP=$(mktemp -d)
function finish {
rm -rf "$TMP"
}
trap finish EXIT
pushd "$TMP"
mkdir -p grpc/gcp
curl https://raw.githubusercontent.com/grpc/grpc-proto/master/grpc/gcp/altscontext.proto > grpc/gcp/altscontext.proto
curl https://raw.githubusercontent.com/grpc/grpc-proto/master/grpc/gcp/handshaker.proto > grpc/gcp/handshaker.proto
curl https://raw.githubusercontent.com/grpc/grpc-proto/master/grpc/gcp/transport_security_common.proto > grpc/gcp/transport_security_common.proto
protoc --go_out=plugins=grpc,paths=source_relative:. -I. grpc/gcp/*.proto
popd
rm -f proto/grpc_gcp/*.pb.go
cp "$TMP"/grpc/gcp/*.pb.go proto/grpc_gcp/

View 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/internal/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
}

141
vendor/google.golang.org/grpc/credentials/alts/utils.go generated vendored Normal file
View 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 alts
import (
"errors"
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
"regexp"
"runtime"
"strings"
"golang.org/x/net/context"
"google.golang.org/grpc/peer"
)
const (
linuxProductNameFile = "/sys/class/dmi/id/product_name"
windowsCheckCommand = "powershell.exe"
windowsCheckCommandArgs = "Get-WmiObject -Class Win32_BIOS"
powershellOutputFilter = "Manufacturer"
windowsManufacturerRegex = ":(.*)"
)
type platformError string
func (k platformError) Error() string {
return fmt.Sprintf("%s is not supported", string(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
}
// AuthInfoFromContext extracts the alts.AuthInfo object from the given context,
// if it exists. This API should be used by gRPC server RPC handlers to get
// information about the communicating peer. For client-side, use grpc.Peer()
// CallOption.
func AuthInfoFromContext(ctx context.Context) (AuthInfo, error) {
p, ok := peer.FromContext(ctx)
if !ok {
return nil, errors.New("no Peer found in Context")
}
return AuthInfoFromPeer(p)
}
// AuthInfoFromPeer extracts the alts.AuthInfo object from the given peer, if it
// exists. This API should be used by gRPC clients after obtaining a peer object
// using the grpc.Peer() CallOption.
func AuthInfoFromPeer(p *peer.Peer) (AuthInfo, error) {
altsAuthInfo, ok := p.AuthInfo.(AuthInfo)
if !ok {
return nil, errors.New("no alts.AuthInfo found in Peer")
}
return altsAuthInfo, nil
}

View file

@ -0,0 +1,139 @@
/*
*
* 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"
"golang.org/x/net/context"
altspb "google.golang.org/grpc/credentials/alts/internal/proto/grpc_gcp"
"google.golang.org/grpc/peer"
)
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
}
}
func TestAuthInfoFromContext(t *testing.T) {
ctx := context.Background()
altsAuthInfo := &fakeALTSAuthInfo{}
p := &peer.Peer{
AuthInfo: altsAuthInfo,
}
for _, tc := range []struct {
desc string
ctx context.Context
success bool
out AuthInfo
}{
{
"working case",
peer.NewContext(ctx, p),
true,
altsAuthInfo,
},
} {
authInfo, err := AuthInfoFromContext(tc.ctx)
if got, want := (err == nil), tc.success; got != want {
t.Errorf("%v: AuthInfoFromContext(_)=(err=nil)=%v, want %v", tc.desc, got, want)
}
if got, want := authInfo, tc.out; got != want {
t.Errorf("%v:, AuthInfoFromContext(_)=(%v, _), want (%v, _)", tc.desc, got, want)
}
}
}
func TestAuthInfoFromPeer(t *testing.T) {
altsAuthInfo := &fakeALTSAuthInfo{}
p := &peer.Peer{
AuthInfo: altsAuthInfo,
}
for _, tc := range []struct {
desc string
p *peer.Peer
success bool
out AuthInfo
}{
{
"working case",
p,
true,
altsAuthInfo,
},
} {
authInfo, err := AuthInfoFromPeer(tc.p)
if got, want := (err == nil), tc.success; got != want {
t.Errorf("%v: AuthInfoFromPeer(_)=(err=nil)=%v, want %v", tc.desc, got, want)
}
if got, want := authInfo, tc.out; got != want {
t.Errorf("%v:, AuthInfoFromPeer(_)=(%v, _), want (%v, _)", tc.desc, got, want)
}
}
}
type fakeALTSAuthInfo struct{}
func (*fakeALTSAuthInfo) AuthType() string { return "" }
func (*fakeALTSAuthInfo) ApplicationProtocol() string { return "" }
func (*fakeALTSAuthInfo) RecordProtocol() string { return "" }
func (*fakeALTSAuthInfo) SecurityLevel() altspb.SecurityLevel {
return altspb.SecurityLevel_SECURITY_NONE
}
func (*fakeALTSAuthInfo) PeerServiceAccount() string { return "" }
func (*fakeALTSAuthInfo) LocalServiceAccount() string { return "" }
func (*fakeALTSAuthInfo) PeerRPCVersions() *altspb.RpcProtocolVersions { return nil }