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

	core 
)
ALTSRecordCrypto is the interface for gRPC ALTS record protocol.
Encrypt encrypts the plaintext and computes the tag (if any) of dst and plaintext. dst and plaintext may fully overlap or not at all.
EncryptionOverhead returns the tag size (if any) in bytes.
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)

MsgLenFieldSize is the byte size of the frame length field of a framed message.
The byte size of the message type field of a framed message.
The bytes size limit for a ALTS record message.
The default bytes size of a ALTS record message.
Message type value included in ALTS record framing.
The initial write buffer size.
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 ( string,  ALTSRecordFunc) error {
	if ,  := protocols[];  {
		return fmt.Errorf("protocol %v is already registered", )
	}
	protocols[] = 
	return nil
}
conn represents a secured connection. It implements the net.Conn interface.
type conn struct {
	net.Conn
buf holds data that has been read from the connection and decrypted, but has not yet been returned by Read.
	buf                []byte
protected holds data read from the network but have not yet been decrypted. This data might not compose a complete frame.
writeBuf is a buffer used to contain encrypted frames before being written to the network.
nextFrame stores the next frame (in protected buffer) info.
overhead is the calculated overhead of each frame.
NewConn creates a new secure channel instance given the other party role and handshaking result.
func ( net.Conn,  core.Side,  string,  []byte,  []byte) (net.Conn, error) {
	 := protocols[]
	if  == nil {
		return nil, fmt.Errorf("negotiated unknown next_protocol %q", )
	}
	,  := (, )
	if  != nil {
		return nil, fmt.Errorf("protocol %q: %v", , )
	}
	 := MsgLenFieldSize + msgTypeFieldSize + .EncryptionOverhead()
	 := altsRecordDefaultLength - 
	var  []byte
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.
		 = make([]byte, 0, 2*altsRecordDefaultLength-1)
	} else {
		 = make([]byte, len())
		copy(, )
	}

	 := &conn{
		Conn:               ,
		crypto:             ,
		payloadLengthLimit: ,
		protected:          ,
		writeBuf:           make([]byte, altsWriteBufferInitialSize),
		nextFrame:          ,
		overhead:           ,
	}
	return , 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 ( *conn) ( []byte) ( int,  error) {
	if len(.buf) == 0 {
		var  []byte
		, .nextFrame,  = ParseFramedMsg(.nextFrame, altsRecordLengthLimit)
		if  != nil {
			return , 
Check whether the next frame to be decrypted has been completely received yet.
		if len() == 0 {
			copy(.protected, .nextFrame)
Always copy next incomplete frame to the beginning of the protected buffer and reset nextFrame to it.
Check whether a complete frame has been received yet.
		for len() == 0 {
			if len(.protected) == cap(.protected) {
				 := make([]byte, len(.protected), cap(.protected)+altsRecordDefaultLength)
				copy(, .protected)
				.protected = 
			}
			,  = .Conn.Read(.protected[len(.protected):min(cap(.protected), len(.protected)+altsRecordDefaultLength)])
			if  != nil {
				return 0, 
			}
			.protected = .protected[:len(.protected)+]
			, .nextFrame,  = ParseFramedMsg(.protected, altsRecordLengthLimit)
			if  != nil {
				return 0, 
			}
Now we have a complete frame, decrypted it.
		 := [MsgLenFieldSize:]
		 := binary.LittleEndian.Uint32([:msgTypeFieldSize])
		if &0xff != altsRecordMsgType {
			return 0, fmt.Errorf("received frame with incorrect message type %v, expected lower byte %v",
				, altsRecordMsgType)
		}
		 := [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.
		.buf,  = .crypto.Decrypt([:0], )
		if  != nil {
			return 0, 
		}
	}

	 = copy(, .buf)
	.buf = .buf[:]
	return , nil
}
Write encrypts, frames, and writes bytes from b to the underlying connection.
func ( *conn) ( []byte) ( int,  error) {
Calculate the output buffer size with framing and encryption overhead.
	 := int(math.Ceil(float64(len()) / float64(.payloadLengthLimit)))
If writeBuf is too small, increase its size up to the maximum size.
	 := len()
	if  > altsWriteBufferMaxSize {
		 = altsWriteBufferMaxSize
		const  = altsWriteBufferMaxSize / altsRecordDefaultLength
		 =  * .payloadLengthLimit
	}
	if len(.writeBuf) <  {
		.writeBuf = make([]byte, )
	}

	for  := 0;  < len();  +=  {
		 :=  + 
		if  > len() {
			 = len()
		}
		 := [:]
		 := 0
		for len() > 0 {
			 := len()
			if  > .payloadLengthLimit {
				 = .payloadLengthLimit
			}
			 := [:]
			 = [:]
Write buffer contains: length, type, payload, and tag if any.
1. Fill in type field.
2. Encrypt the payload and create a tag if any.
			,  = .crypto.Encrypt([:msgTypeFieldSize], )
			if  != nil {
				return , 
			}
3. Fill in the size field.
			binary.LittleEndian.PutUint32(.writeBuf[:], uint32(len()))
4. Increase writeBufIndex.
			 += len() + .overhead
		}
		,  := .Conn.Write(.writeBuf[:])
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.
			 := int(math.Floor(float64() / float64(altsRecordDefaultLength)))
			return  + *.payloadLengthLimit, 
		}
	}
	return , nil
}

func (,  int) int {
	if  <  {
		return 
	}
	return