mirror of https://github.com/kubernetes/kops.git
206 lines
6.4 KiB
Go
206 lines
6.4 KiB
Go
package mesh
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"encoding/binary"
|
|
"encoding/gob"
|
|
"fmt"
|
|
"io"
|
|
"sync"
|
|
|
|
"golang.org/x/crypto/nacl/box"
|
|
"golang.org/x/crypto/nacl/secretbox"
|
|
)
|
|
|
|
// MaxTCPMsgSize is the hard limit on sends and receives. Larger messages will
|
|
// result in errors. This applies to the LengthPrefixTCP{Sender,Receiver} i.e.
|
|
// V2 of the protocol.
|
|
const maxTCPMsgSize = 10 * 1024 * 1024
|
|
|
|
// GenerateKeyPair is used during encrypted protocol introduction.
|
|
func generateKeyPair() (publicKey, privateKey *[32]byte, err error) {
|
|
return box.GenerateKey(rand.Reader)
|
|
}
|
|
|
|
// FormSessionKey is used during encrypted protocol introduction.
|
|
func formSessionKey(remotePublicKey, localPrivateKey *[32]byte, secretKey []byte) *[32]byte {
|
|
var sharedKey [32]byte
|
|
box.Precompute(&sharedKey, remotePublicKey, localPrivateKey)
|
|
sharedKeySlice := sharedKey[:]
|
|
sharedKeySlice = append(sharedKeySlice, secretKey...)
|
|
sessionKey := sha256.Sum256(sharedKeySlice)
|
|
return &sessionKey
|
|
}
|
|
|
|
// TCP Senders/Receivers
|
|
|
|
// TCPCryptoState stores session key, nonce, and sequence state.
|
|
//
|
|
// The lowest 64 bits of the nonce contain the message sequence number. The
|
|
// top most bit indicates the connection polarity at the sender - '1' for
|
|
// outbound; the next indicates protocol type - '1' for TCP. The remaining 126
|
|
// bits are zero. The polarity is needed so that the two ends of a connection
|
|
// do not use the same nonces; the protocol type so that the TCP connection
|
|
// nonces are distinct from nonces used by overlay connections, if they share
|
|
// the session key. This is a requirement of the NaCl Security Model; see
|
|
// http://nacl.cr.yp.to/box.html.
|
|
type tcpCryptoState struct {
|
|
sessionKey *[32]byte
|
|
nonce [24]byte
|
|
seqNo uint64
|
|
}
|
|
|
|
// NewTCPCryptoState returns a valid TCPCryptoState.
|
|
func newTCPCryptoState(sessionKey *[32]byte, outbound bool) *tcpCryptoState {
|
|
s := &tcpCryptoState{sessionKey: sessionKey}
|
|
if outbound {
|
|
s.nonce[0] |= (1 << 7)
|
|
}
|
|
s.nonce[0] |= (1 << 6)
|
|
return s
|
|
}
|
|
|
|
func (s *tcpCryptoState) advance() {
|
|
s.seqNo++
|
|
binary.BigEndian.PutUint64(s.nonce[16:24], s.seqNo)
|
|
}
|
|
|
|
// TCPSender describes anything that can send byte buffers.
|
|
// It abstracts over the different protocol version senders.
|
|
type tcpSender interface {
|
|
Send([]byte) error
|
|
}
|
|
|
|
// GobTCPSender implements TCPSender and is used in the V1 protocol.
|
|
type gobTCPSender struct {
|
|
encoder *gob.Encoder
|
|
}
|
|
|
|
func newGobTCPSender(encoder *gob.Encoder) *gobTCPSender {
|
|
return &gobTCPSender{encoder: encoder}
|
|
}
|
|
|
|
// Send implements TCPSender by encoding the msg.
|
|
func (sender *gobTCPSender) Send(msg []byte) error {
|
|
return sender.encoder.Encode(msg)
|
|
}
|
|
|
|
// LengthPrefixTCPSender implements TCPSender and is used in the V2 protocol.
|
|
type lengthPrefixTCPSender struct {
|
|
writer io.Writer
|
|
}
|
|
|
|
func newLengthPrefixTCPSender(writer io.Writer) *lengthPrefixTCPSender {
|
|
return &lengthPrefixTCPSender{writer: writer}
|
|
}
|
|
|
|
// Send implements TCPSender by writing the size of the msg as a big-endian
|
|
// uint32 before the msg. msgs larger than MaxTCPMsgSize are rejected.
|
|
func (sender *lengthPrefixTCPSender) Send(msg []byte) error {
|
|
l := len(msg)
|
|
if l > maxTCPMsgSize {
|
|
return fmt.Errorf("outgoing message exceeds maximum size: %d > %d", l, maxTCPMsgSize)
|
|
}
|
|
// We copy the message so we can send it in a single Write
|
|
// operation, thus making this thread-safe without locking.
|
|
prefixedMsg := make([]byte, 4+l)
|
|
binary.BigEndian.PutUint32(prefixedMsg, uint32(l))
|
|
copy(prefixedMsg[4:], msg)
|
|
_, err := sender.writer.Write(prefixedMsg)
|
|
return err
|
|
}
|
|
|
|
// Implement TCPSender by wrapping an existing TCPSender with tcpCryptoState.
|
|
type encryptedTCPSender struct {
|
|
sync.RWMutex
|
|
sender tcpSender
|
|
state *tcpCryptoState
|
|
}
|
|
|
|
func newEncryptedTCPSender(sender tcpSender, sessionKey *[32]byte, outbound bool) *encryptedTCPSender {
|
|
return &encryptedTCPSender{sender: sender, state: newTCPCryptoState(sessionKey, outbound)}
|
|
}
|
|
|
|
// Send implements TCPSender by sealing and sending the msg as-is.
|
|
func (sender *encryptedTCPSender) Send(msg []byte) error {
|
|
sender.Lock()
|
|
defer sender.Unlock()
|
|
encodedMsg := secretbox.Seal(nil, msg, &sender.state.nonce, sender.state.sessionKey)
|
|
sender.state.advance()
|
|
return sender.sender.Send(encodedMsg)
|
|
}
|
|
|
|
// tcpReceiver describes anything that can receive byte buffers.
|
|
// It abstracts over the different protocol version receivers.
|
|
type tcpReceiver interface {
|
|
Receive() ([]byte, error)
|
|
}
|
|
|
|
// gobTCPReceiver implements TCPReceiver and is used in the V1 protocol.
|
|
type gobTCPReceiver struct {
|
|
decoder *gob.Decoder
|
|
}
|
|
|
|
func newGobTCPReceiver(decoder *gob.Decoder) *gobTCPReceiver {
|
|
return &gobTCPReceiver{decoder: decoder}
|
|
}
|
|
|
|
// Receive implements TCPReciever by Gob decoding into a byte slice directly.
|
|
func (receiver *gobTCPReceiver) Receive() ([]byte, error) {
|
|
var msg []byte
|
|
err := receiver.decoder.Decode(&msg)
|
|
return msg, err
|
|
}
|
|
|
|
// lengthPrefixTCPReceiver implements TCPReceiver, used in the V2 protocol.
|
|
type lengthPrefixTCPReceiver struct {
|
|
reader io.Reader
|
|
}
|
|
|
|
func newLengthPrefixTCPReceiver(reader io.Reader) *lengthPrefixTCPReceiver {
|
|
return &lengthPrefixTCPReceiver{reader: reader}
|
|
}
|
|
|
|
// Receive implements TCPReceiver by making a length-limited read into a byte buffer.
|
|
func (receiver *lengthPrefixTCPReceiver) Receive() ([]byte, error) {
|
|
lenPrefix := make([]byte, 4)
|
|
if _, err := io.ReadFull(receiver.reader, lenPrefix); err != nil {
|
|
return nil, err
|
|
}
|
|
l := binary.BigEndian.Uint32(lenPrefix)
|
|
if l > maxTCPMsgSize {
|
|
return nil, fmt.Errorf("incoming message exceeds maximum size: %d > %d", l, maxTCPMsgSize)
|
|
}
|
|
msg := make([]byte, l)
|
|
_, err := io.ReadFull(receiver.reader, msg)
|
|
return msg, err
|
|
}
|
|
|
|
// encryptedTCPReceiver implements TCPReceiver by wrapping a TCPReceiver with TCPCryptoState.
|
|
type encryptedTCPReceiver struct {
|
|
receiver tcpReceiver
|
|
state *tcpCryptoState
|
|
}
|
|
|
|
func newEncryptedTCPReceiver(receiver tcpReceiver, sessionKey *[32]byte, outbound bool) *encryptedTCPReceiver {
|
|
return &encryptedTCPReceiver{receiver: receiver, state: newTCPCryptoState(sessionKey, !outbound)}
|
|
}
|
|
|
|
// Receive implements TCPReceiver by reading from the wrapped TCPReceiver and
|
|
// unboxing the encrypted message, returning the decoded message.
|
|
func (receiver *encryptedTCPReceiver) Receive() ([]byte, error) {
|
|
msg, err := receiver.receiver.Receive()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
decodedMsg, success := secretbox.Open(nil, msg, &receiver.state.nonce, receiver.state.sessionKey)
|
|
if !success {
|
|
return nil, fmt.Errorf("Unable to decrypt TCP msg")
|
|
}
|
|
|
|
receiver.state.advance()
|
|
return decodedMsg, nil
|
|
}
|