stream

package module
v0.0.0-...-580d263 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 23, 2023 License: MIT Imports: 13 Imported by: 1

README

encrypted-stream

GoDoc GitHub license Go Report Card Build Status PRs Welcome

Encrypted-stream is a Golang library that transforms any net.Conn or io.ReadWriter stream to an encrypted and/or authenticated stream.

  • The encrypted stream implements net.Conn and io.ReadWriter and can be used as drop-in replacement.

  • Works with any encryption, authentication, or authenticated encryption algorithm or even arbitrary transformation. Only a cipher that implements encrypt/decrypt needs to be provided. XSalsa20-Poly1305 and AES-GCM are provided as reference cipher.

  • The encrypted stream only adds a small constant memory overhead compared to the original stream.

Note: this library does not handle handshake or key exchange. Handshake should be done separately before using this library to compute a shared key.

Documentation

Full documentation can be found at GoDoc.

Usage

Assume you have a net.Conn and you want to transform it into an encrypted net.Conn:

conn, err := net.Dial("tcp", "host:port")

You first need to have a shared key at both side of the connection (e.g. derived from key exchange algorithm). Then all you need to do is to choose or implements a cipher:

encryptedConn, err := stream.NewEncryptedStream(conn, &stream.Config{
  Cipher: stream.NewXSalsa20Poly1305Cipher(&key),
  SequentialNonce: true, // only when key is unique for every stream
  Initiator: true, // only on the dialer side
})

Now you can use encryptedConn just like conn, but everything is encrypted and authenticated.

See stream_test.go for complete example and benchmark with TCP connection.

Benchmark

$ go test -v -bench=. -run=^$
goos: darwin
goarch: amd64
pkg: github.com/qsocket/encrypted-stream
BenchmarkPipeXSalsa20Poly1305-12    	    4064	    266725 ns/op	 491.41 MB/s	       3 B/op	       0 allocs/op
BenchmarkPipeAESGCM128-12           	   16195	     71669 ns/op	1828.86 MB/s	       0 B/op	       0 allocs/op
BenchmarkPipeAESGCM256-12           	   14328	     83337 ns/op	1572.79 MB/s	       0 B/op	       0 allocs/op
BenchmarkTCPXSalsa20Poly1305-12     	    6489	    185980 ns/op	 704.76 MB/s	       0 B/op	       0 allocs/op
BenchmarkTCPAESGCM128-12            	   20089	     59684 ns/op	2196.08 MB/s	       0 B/op	       0 allocs/op
BenchmarkTCPAESGCM256-12            	   17656	     67721 ns/op	1935.48 MB/s	       0 B/op	       0 allocs/op
PASS
ok  	github.com/qsocket/encrypted-stream	9.997s

Documentation

Overview

Package stream is a Golang library that transforms any net.Conn or io.ReadWriter stream to an encrypted and/or authenticated stream.

1. The encrypted stream implements net.Conn and io.ReadWriter and can be used as drop-in replacement.

2. Works with any encryption, authentication, or authenticated encryption algorithm or even arbitrary transformation. Only a cipher that implements encrypt/decrypt needs to be provided. XSalsa20-Poly1305 and AES-GCM are provided as reference cipher.

3. The encrypted stream only adds a small constant memory overhead compared to the original stream.

Note: this library does not handle handshake or key exchange. Handshake should be done separately before using this library to compute a shared key.

Example
package main

import (
	"crypto/rand"
	"net"

	stream "github.com/qsocket/encrypted-stream"
)

func main() {
	// We use a net.Conn as an example.
	conn, err := net.Dial("tcp", "golang.org:80")
	if err != nil {
		panic(err)
	}

	// In this example we treat key as a prior knowledge. In actual usage you can
	// do handshake here using the original stream and compute shared key before
	// creating encrypted stream from it.
	var key [32]byte
	_, err = rand.Read(key[:])
	if err != nil {
		panic(err)
	}

	config := &stream.Config{
		Cipher:          stream.NewXSalsa20Poly1305Cipher(&key),
		SequentialNonce: true, // only when key is unique for every stream
		Initiator:       true, // only on the dialer side
	}

	// Create an encrypted stream from a conn.
	encryptedConn, err := stream.NewEncryptedStream(conn, config)
	if err != nil {
		panic(err)
	}

	// Now you can use encryptedConn just like a regular conn
	_, err = encryptedConn.Write([]byte("hello world"))
	if err != nil {
		panic(err)
	}
}
Output:

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrMaxNonce indicates the max allowed nonce is reach. If this happends, a
	// new stream with different key should be created.
	ErrMaxNonce = errors.New("max nonce reached")

	// ErrWrongNonceInitiator indicates a nonce with the wrong party is received,
	// i.e. initiator receives a nonce from initiator, or responder receives a
	// nonce from responder. It is either a configuration error (e.g. both party
	// set initiator to the same value), or a middle man is performing reflection
	// attack.
	ErrWrongNonceInitiator = errors.New("wrong nonce direction")

	// ErrWrongNonceSequential indicates a nonce with the wrong value is received.
	// It is either a configuration error (e.g. one side set sequentialNonce to
	// true, the other side set to false), or a middle man is performing replay,
	// re-order or packet drop attack.
	ErrWrongNonceSequential = errors.New("wrong nonce value")
)

Functions

This section is empty.

Types

type Cipher

type Cipher interface {
	// Encrypt encrypts a plaintext to ciphertext. Returns ciphertext slice
	// whose length should be equal to ciphertext length. Input buffer ciphertext
	// has enough length for encrypted plaintext, and the length satisfy:
	// 	len(ciphertext) == MaxChunkSize + MaxOverheadSize
	// 	len(plaintext) <= MaxChunkSize
	Encrypt(ciphertext, plaintext, nonce []byte) ([]byte, error)

	// Decrypt decrypts a ciphertext to plaintext. Returns plaintext slice
	// whose length should be equal to plaintext length. Input buffer plaintext
	// has enough length for decrypted ciphertext, and the length satisfy:
	//	len(plaintext) == MaxChunkSize
	//	len(ciphertext) <= MaxChunkSize + MaxOverheadSize
	Decrypt(plaintext, ciphertext, nonce []byte) ([]byte, error)

	// MaxOverhead is the max number of bytes overhead of ciphertext compared to
	// plaintext. It is only used to determine internal buffer size, so
	// overestimate is ok.
	MaxOverhead() int

	// NonceSize is the nonce size in bytes.
	NonceSize() int
}

Cipher provides encrypt and decrypt function of a slice data.

type Config

type Config struct {
	// Cipher is used to encrypt and decrypt data.
	Cipher Cipher

	// MaxChunkSize is the max number of bytes that will be encrypted and write to
	// underlying stream in a single chunk. If zero, default value (65535) will be
	// used.
	MaxChunkSize int `default:"30"`

	// Initiator indicates the direction of the stream (initiator or responder).
	// Two sides of the stream should set this to different value (i.e. one stream
	// initiator and one stream responder) unless DisableNonceVerification is
	// true.
	Initiator bool

	// Use sequential nonce instead of random nonce to prevent replay, re-order
	// and packet drop attack. For cipher with short nonce (e.g. AES-GCM),
	// sequential nonce should be used to prevent nonce reuse if large amount of
	// data is transmitted in the same stream. Both sides of the stream should set
	// this to the same value unless DisableNonceVerification is true. IMPORTANT:
	// Enable sequential nonce only when key is unique for every stream, otherwise
	// key will be leaked.
	SequentialNonce bool

	// Disable nonce verification during decryption. Setting this to true will
	// make the stream vulnerable to reflection, replay, re-order and packet drop
	// attack. Do not set it to true unless you have a strong reason.
	DisableNonceVerification bool
}

Config is the configuration for encrypted stream.

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig returns the default config.

func (*Config) Verify

func (config *Config) Verify() error

Verify checks whether a config is valid.

type CryptoAEADCipher

type CryptoAEADCipher struct {
	// contains filtered or unexported fields
}

CryptoAEADCipher is a wrapper to crypto/cipher AEAD interface and implements Cipher interface.

func NewAESGCMCipher

func NewAESGCMCipher(key []byte) (*CryptoAEADCipher, error)

NewAESGCMCipher creates a 128-bit (16 bytes key) or 256-bit (32 bytes key) AES block cipher wrapped in Galois Counter Mode with the standard nonce length. For best security, every stream should have a unique key.

func NewCryptoAEADCipher

func NewCryptoAEADCipher(aead cipher.AEAD) *CryptoAEADCipher

NewCryptoAEADCipher converts a crypto/cipher AEAD to Cipher.

func (*CryptoAEADCipher) Decrypt

func (c *CryptoAEADCipher) Decrypt(plaintext, ciphertext, nonce []byte) ([]byte, error)

Decrypt implements Cipher.

func (*CryptoAEADCipher) Encrypt

func (c *CryptoAEADCipher) Encrypt(ciphertext, plaintext, nonce []byte) ([]byte, error)

Encrypt implements Cipher.

func (*CryptoAEADCipher) MaxOverhead

func (c *CryptoAEADCipher) MaxOverhead() int

MaxOverhead implements Cipher.

func (*CryptoAEADCipher) NonceSize

func (c *CryptoAEADCipher) NonceSize() int

NonceSize implements Cipher.

type Decoder

type Decoder struct {
	// contains filtered or unexported fields
}

Decoder provides decode function of a slice data.

func NewDecoder

func NewDecoder(cipher Cipher, initiator, sequentialNonce, disableNonceVerification bool) (*Decoder, error)

NewDecoder creates a Decoder with given cipher and config.

func (*Decoder) Decode

func (d *Decoder) Decode(plaintext, ciphertext []byte) ([]byte, error)

Decode decodes a nonce + ciphertext to plaintext. When sequential nonce is true, Decode is not thread safe and should not be called concurrently.

type Encoder

type Encoder struct {
	// contains filtered or unexported fields
}

Encoder provides encode function of a slice data.

func NewEncoder

func NewEncoder(cipher Cipher, initiator, sequentialNonce bool) (*Encoder, error)

NewEncoder creates a Encoder with given cipher and config.

func (*Encoder) Encode

func (e *Encoder) Encode(ciphertext, plaintext []byte) ([]byte, error)

Encode encodes a plaintext to nonce + ciphertext. When sequential nonce is true, Encode is not thread safe and should not be called concurrently.

type EncryptedStream

type EncryptedStream struct {
	// contains filtered or unexported fields
}

EncryptedStream is an encrypted stream. Data are encrypted before writing to underlying stream, and are decrypted after reading from underlying stream.

func NewEncryptedStream

func NewEncryptedStream(stream io.ReadWriter, config *Config) (*EncryptedStream, error)

NewEncryptedStream creates an EncryptedStream with a given ReadWriter and config.

func (*EncryptedStream) Close

func (es *EncryptedStream) Close() error

Close implements net.Conn and io.Closer. Will call underlying stream's Close() method if it has one.

func (*EncryptedStream) IsClosed

func (es *EncryptedStream) IsClosed() bool

IsClosed returns whether the EncryptedStream is closed.

func (*EncryptedStream) LocalAddr

func (es *EncryptedStream) LocalAddr() net.Addr

LocalAddr implements net.Conn. Will call underlying stream's LocalAddr() method if it has one, otherwise will return nil.

func (*EncryptedStream) Read

func (es *EncryptedStream) Read(b []byte) (int, error)

Read implements net.Conn and io.Reader

func (*EncryptedStream) RemoteAddr

func (es *EncryptedStream) RemoteAddr() net.Addr

RemoteAddr implements net.Conn. Will call underlying stream's RemoteAddr() method if it has one, otherwise will return nil.

func (*EncryptedStream) SetDeadline

func (es *EncryptedStream) SetDeadline(t time.Time) error

SetDeadline implements net.Conn. Will call underlying stream's SetDeadline() method if it has one, otherwise will return nil.

func (*EncryptedStream) SetReadDeadline

func (es *EncryptedStream) SetReadDeadline(t time.Time) error

SetReadDeadline implements net.Conn. Will call underlying stream's SetReadDeadline() method if it has one, otherwise will return nil.

func (*EncryptedStream) SetWriteDeadline

func (es *EncryptedStream) SetWriteDeadline(t time.Time) error

SetWriteDeadline implements net.Conn. Will call underlying stream's SetWriteDeadline() method if it has one, otherwise will return nil.

func (*EncryptedStream) Write

func (es *EncryptedStream) Write(b []byte) (int, error)

Write implements net.Conn and io.Writer

type XSalsa20Poly1305Cipher

type XSalsa20Poly1305Cipher struct {
	// contains filtered or unexported fields
}

XSalsa20Poly1305Cipher is an AEAD cipher that uses XSalsa20 and Poly1305 to encrypt and authenticate messages. The ciphertext it produces contains 24 bytes of random nonce, followed by n+16 bytes of authenticated encrypted data, where n is the plaintext size.

func NewXSalsa20Poly1305Cipher

func NewXSalsa20Poly1305Cipher(key *[32]byte) *XSalsa20Poly1305Cipher

NewXSalsa20Poly1305Cipher creates a XSalsa20Poly1305Cipher with a given key. For best security, every stream should have a unique key.

func (*XSalsa20Poly1305Cipher) Decrypt

func (c *XSalsa20Poly1305Cipher) Decrypt(plaintext, ciphertext, nonce []byte) ([]byte, error)

Decrypt implements Cipher.

func (*XSalsa20Poly1305Cipher) Encrypt

func (c *XSalsa20Poly1305Cipher) Encrypt(ciphertext, plaintext, nonce []byte) ([]byte, error)

Encrypt implements Cipher.

func (*XSalsa20Poly1305Cipher) MaxOverhead

func (c *XSalsa20Poly1305Cipher) MaxOverhead() int

MaxOverhead implements Cipher.

func (*XSalsa20Poly1305Cipher) NonceSize

func (c *XSalsa20Poly1305Cipher) NonceSize() int

NonceSize implements Cipher.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL