* * 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 (
	
	
	
	
	
	

	grpc 
	
	
	core 
	
	
	altsgrpc 
	altspb 
)

The maximum byte size of receive frames.
	frameLimit              = 64 * 1024 // 64 KB
maxPendingHandshakes represents the maximum number of concurrent handshakes.
ALTS handshaker protocols.
		rekeyRecordProtocolName: func( core.Side,  []byte) (conn.ALTSRecordCrypto, error) {
			return conn.NewAES128GCMRekey(, )
		},
control number of concurrent created (but not closed) handshakers.
errDropped occurs when maxPendingHandshakes is reached.
errOutOfBound occurs when the handshake service returns a consumed bytes value larger than the buffer that was passed to it originally.
	errOutOfBound = errors.New("handshaker service consumed bytes value is out-of-bound")
)

func () {
	for ,  := range altsRecordFuncs {
		if  := conn.RegisterProtocol(, );  != nil {
			panic()
		}
	}
}

func () bool {
If we need n to be configurable, we can pass it as an argument.
	 := int64(1)
	 := maxPendingHandshakes-concurrentHandshakes >= 
	if  {
		concurrentHandshakes += 
	}
	mu.Unlock()
	return 
}

func () {
If we need n to be configurable, we can pass it as an argument.
	 := int64(1)
	concurrentHandshakes -= 
	if concurrentHandshakes < 0 {
		mu.Unlock()
		panic("bad release")
	}
	mu.Unlock()
}
ClientHandshakerOptions contains the client handshaker options that can provided by the caller.
ClientIdentity is the handshaker client local identity.
TargetName is the server service account name for secure name checking.
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.
RPCVersions specifies the gRPC versions accepted by the client.
ServerHandshakerOptions contains the server handshaker options that can provided by the caller.
RPCVersions specifies the gRPC versions accepted by the server.
DefaultClientHandshakerOptions returns the default client handshaker options.
DefaultServerHandshakerOptions returns the default client handshaker options.
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.
RPC stream used to access the ALTS Handshaker service.
the connection to the peer.
client handshake options.
server handshake options.
defines the side doing the handshake, client or server.
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 ( context.Context,  *grpc.ClientConn,  net.Conn,  *ClientHandshakerOptions) (core.Handshaker, error) {
	,  := altsgrpc.NewHandshakerServiceClient().DoHandshake(, grpc.WaitForReady(true))
	if  != nil {
		return nil, 
	}
	return &altsHandshaker{
		stream:     ,
		conn:       ,
		clientOpts: ,
		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 ( context.Context,  *grpc.ClientConn,  net.Conn,  *ServerHandshakerOptions) (core.Handshaker, error) {
	,  := altsgrpc.NewHandshakerServiceClient().DoHandshake(, grpc.WaitForReady(true))
	if  != nil {
		return nil, 
	}
	return &altsHandshaker{
		stream:     ,
		conn:       ,
		serverOpts: ,
		side:       core.ServerSide,
	}, nil
}
ClientHandshake starts and completes a client ALTS handshaking for GCP. Once done, ClientHandshake returns a secure connection.
func ( *altsHandshaker) ( context.Context) (net.Conn, credentials.AuthInfo, error) {
	if !acquire() {
		return nil, nil, errDropped
	}
	defer release()

	if .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.
ServerHandshake starts and completes a server ALTS handshaking for GCP. Once done, ServerHandshake returns a secure connection.
func ( *altsHandshaker) ( context.Context) (net.Conn, credentials.AuthInfo, error) {
	if !acquire() {
		return nil, nil, errDropped
	}
	defer release()

	if .side != core.ServerSide {
		return nil, nil, errors.New("only handshakers created using NewServerHandshaker can perform a server handshaker")
	}

	 := make([]byte, frameLimit)
	,  := .conn.Read()
	if  != nil {
		return nil, nil, 
	}
Prepare server parameters. TODO: currently only ALTS parameters are provided. Might need to use more options in the future.
Check of the returned status is an error.
	if .GetStatus() != nil {
		if ,  := .GetStatus().Code, uint32(codes.OK);  !=  {
			return nil, nil, fmt.Errorf("%v", .GetStatus().Details)
		}
	}

	var  []byte
	if .GetServerStart() != nil {
		if .GetBytesConsumed() > uint32(len(.GetServerStart().GetInBytes())) {
			return nil, nil, errOutOfBound
		}
		 = .GetServerStart().GetInBytes()[.GetBytesConsumed():]
	}
	, ,  := .processUntilDone(, )
	if  != nil {
		return nil, nil, 
The handshaker returns a 128 bytes key. It should be truncated based on the returned record protocol.
	,  := keyLength[.RecordProtocol]
	if ! {
		return nil, nil, fmt.Errorf("unknown resulted record protocol %v", .RecordProtocol)
	}
	,  := conn.NewConn(.conn, .side, .GetRecordProtocol(), .KeyData[:], )
	if  != nil {
		return nil, nil, 
	}
	return , , nil
}

func ( *altsHandshaker) ( *altspb.HandshakerReq) (*altspb.HandshakerResp, error) {
	if  := .stream.Send();  != nil {
		return nil, 
	}
	,  := .stream.Recv()
	if  != nil {
		return nil, 
	}
	return , 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 ( *altsHandshaker) ( *altspb.HandshakerResp,  []byte) (*altspb.HandshakerResult, []byte, error) {
	for {
		if len(.OutFrames) > 0 {
			if ,  := .conn.Write(.OutFrames);  != nil {
				return nil, nil, 
			}
		}
		if .Result != nil {
			return .Result, , nil
		}
		 := make([]byte, frameLimit)
		,  := .conn.Read()
		if  != nil &&  != io.EOF {
			return nil, nil, 
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(.OutFrames) == 0 &&  == 0 {
			return nil, nil, core.PeerNotRespondingError
Append extra bytes from the previous interaction with the handshaker service with the current buffer read from conn.
From here on, p and extra point to the same slice.
Set extra based on handshaker service response.
		if .GetBytesConsumed() > uint32(len()) {
			return nil, nil, errOutOfBound
		}
		 = [.GetBytesConsumed():]
	}
}
Close terminates the Handshaker. It should be called when the caller obtains the secure connection.