stream

package module
v0.0.0-...-60d12ed Latest Latest
Warning

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

Go to latest
Published: Nov 8, 2022 License: Apache-2.0 Imports: 15 Imported by: 0

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/nknorg/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/nknorg/encrypted-stream	9.997s

Contributing

Can I submit a bug, suggestion or feature request?

Yes. Please open an issue for that.

Can I contribute patches?

Yes, we appreciate your help! To make contributions, please fork the repo, push your changes to the forked repo with signed-off commits, and open a pull request here.

Please sign off your commit. This means adding a line "Signed-off-by: Name " at the end of each commit, indicating that you wrote the code and have the right to pass it on as an open source patch. This can be done automatically by adding -s when committing:

git commit -s

Community

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 "gitlab.com/CoiaPrant/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")
)
View Source
var DefaultAllocer = &defaultAllocer{}

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

	// 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

	Allocer MemoryAllocer
}

Config is the configuration for encrypted stream.

func MergeConfig

func MergeConfig(base, conf *Config) (*Config, error)

MergeConfig merges a given config with the default config recursively. Any non zero value fields will override 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 NewChacha20Poly1305Cipher

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

NewChacha20Poly1305Cipher creates 256-bit (32 bytes key)

func NewCryptoAEADCipher

func NewCryptoAEADCipher(aead cipher.AEAD) *CryptoAEADCipher

NewCryptoAEADCipher converts a crypto/cipher AEAD to Cipher.

func NewXChacha20Poly1305Cipher

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

NewChacha20Poly1305Cipher creates 256-bit (32 bytes key)

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() (err 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 MemoryAllocer

type MemoryAllocer interface {
	Get(n int) []byte
	Put(b []byte) error
}

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