babble

package module
v0.0.0 Latest Latest
Warning

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

Go to latest
Published: Jun 16, 2020 License: MIT Imports: 13 Imported by: 0

README

Babble

Build Status codecov Go Report Card GoDoc

Babble is the Go implementation of the Noise Protocol Framework.

Being a framework, the essence is to have the ability to construct new protocols by applying any cryptographically secure functions. With extensibility in mind, babble makes it easy to add any new patterns, cipher functions, hash functions, and DH functions.

The current built-in components are summerized as follows,

  • DH curves: curve448, curve25519 and secp256k1.
  • Ciphers: ChaCha20-Poly1305 and AESGCM.
  • Hash functions: SHA256, SHA512, BLAKE2b and BLAKE2s.
  • Patterns: all the patterns defined here, with PSK mode supported.

Note: the current version doesn't implement the fallback mode.

Usage

To use, download the package,

go get -u "github.com/crypto-y/babble"

In addition to the main package babble, there are five packages which can be used for customization, see Extentable Components.

"github.com/crypto-y/babble/cipher"
"github.com/crypto-y/babble/dh"
"github.com/crypto-y/babble/hash"
"github.com/crypto-y/babble/rekey"
"github.com/crypto-y/babble/pattern"

WARNING: The Go's implementation of AESGCM might be vunerable to side channel attack, please read the documentation if you plan to use it.

There are two methods can be used for contructing new handshake state, NewProtocol and NewProtocolWithConfig.

NewProtocol
func NewProtocol(name, prologue string, initiator bool) (*HandshakeState, error)

The NewProtocol is used for quickly creating a new HandshakeState using the name and prologue, along with an initiator specifying whether the caller is an initiator or a responder.

// creates a new handshake state using pattern NN, curve 25519, cipher
// ChaChaPoly and hash function BLAKE2s.
p, _ := babble.NewProtocol("Noise_NN_25519_ChaChaPoly_BLAKE2s", "Demo", true)

This function uses a default rekeyer, which rotates the cipher key every 10000 encryption/decription and resets the nonce to be zero, read this documentation for detailed specs.

While it's convenient to create a protocol with three parameters, it's not achieved without a cost, in which only a limited number of patterns are supported when using NewProtocol. In particular,

  • The pre-message pattern is not supported, as it requires either one or both static public keys to be known before the handshake.
  • Pre-shared symmetric key, PSK, is not supported, as it required both parties to specify a PSK before the handshake.
// returns an error if the NewProtocol is used with pattern which has
// pre-message or psks.
_, err := babble.NewProtocol("Noise_N_25519_ChaChaPoly_BLAKE2s", "Demo", true)
fmt.Println(err)  // missing key: remote static key

_, err := babble.NewProtocol("Noise_NNpsk0_25519_ChaChaPoly_BLAKE2s", "Demo", true)
fmt.Println(err)  // psk mode: expected to have 1 psks, got 0

Patterns supported using NewProtocol,

  • NN
  • NX, NX1
  • XN, X1N
  • IN, I1N
  • XX, X1X, XX1, X1X1
  • IX, I1X, IX1, I1X1

For full support, use NewProtocolWithConfig instead.

NewProtocolWithConfig
func NewProtocolWithConfig(config *ProtocolConfig) (*HandshakeState, error)

NewProtocolWithConfig takes a *ProtocolConfig to create a handshake state.

// create a config first
cfg := &babble.ProtocolConfig{
    Name: "Noise_NN_25519_ChaChaPoly_BLAKE2s",
    Initiator: true,
    Prologue: "Demo",
}

// use the config to construct a handshake state.
// this is equivalent of calling
// NewProtocol("Noise_NN_25519_ChaChaPoly_BLAKE2s", "Demo", true)
p, _ := babble.NewProtocolWithConfig(cfg)

Specifying a local static private key and a remote static public key,

// decode hex into binary, note that the local static key(s) is a private key, and the remote static key(rs) is a public key.
s, _ := hex.DecodeString(
  "a8abababababababababababababababababababababababababababababab6b")
rs, _ := hex.DecodeString(
  "c3c637648530e306e1115428acc44d0f0502615ee23ec1de0e59c5a148e9a30d")

cfg := &babble.ProtocolConfig{
    Name:            "Noise_KK_25519_ChaChaPoly_BLAKE2s",
    Initiator:       true,
    Prologue:        "Demo",
    LocalStaticPriv: s,
    RemoteStaticPub: rs,
}
p, _ := babble.NewProtocolWithConfig(cfg)

Specifying PSKs,

// decode hex into binary
psk0, _ := hex.DecodeString(
  "c3c637648530e306e1115428acc44d0f0502615ee23ec1de0e59c5a148e9a30d")
psk1, _ := hex.DecodeString(
  "2d0326b5ea11ba9330949dc4e816735615d718551aa9e777f25941c95d7899eb")
// put psks into a slice
psks := [][]byte{psk0, psk1}

cfg := &babble.ProtocolConfig{
    Name: "Noise_NNpsk0+psk1_25519_ChaChaPoly_BLAKE2s",
    Initiator: true,
    Prologue: "Demo",
    Psks: psks,
}
p, _:= babble.NewProtocolWithConfig(cfg)

Specifying default Rekey behavior,

// This config will set the rekeyer to rotate the cipher key every
// 1000 messages and won't reset the nonce.
rkCfg := &babble.DefaultRekeyerConfig{
    Interval: 1000,
    ResetNonce: false,
}

cfg := &babble.ProtocolConfig{
    Name: "Noise_NN_25519_ChaChaPoly_BLAKE2s",
    Initiator: true,
    Prologue: "Demo",
    RekeyerConfig: rkCfg,
}
p, _ := babble.NewProtocolWithConfig(cfg)

You can also specify a customized rekeyer by defining your own rules on when and how the cipher key should be reset. Read this documentation for more details.

Check here for the full list of parameters in the ProtocolConfig.

GetInfo

Once created, the GetInfo method can be handy to monitor the internal state of the current handshake.

// GetInfo will return the internal state info of the handshake
info, _ := p.GetInfo()
fmt.Printf("%s", info)

which prints the following result,

{
	"chaining_key": "38a1b63073db5d5a3a4007b51e83c41598ea2f67e2389e121c56f3a1462d98aa",
	"cipher_key": "0000000000000000000000000000000000000000000000000000000000000000",
	"digest": "ec62085a3ed4240d70240150cc3f98a170fd781cf11c151c65776b04e67be173",
	"finished": false,
	"initiator": true,
	"key_pair": {
		"local_static_priv": "",
		"local_static_pub": "",
		"local_ephemeral_priv": "",
		"local_ephemeral_pub": "",
		"remote_ephemeral_pub": "",
		"remote_static_pub": ""
	},
	"nonce": 0,
	"pattern": {
		"psk_mode": {
			"mode": false,
			"psks": {}
		},
		"name": "NN",
		"pre_message": {},
		"message": {
			"0": "->, e",
			"1": "<-, e, ee"
		},
		"index_processed": -1
	},
	"prologue": "Demo",
	"send_cipher": {
		"key": "",
		"nonce": 0
	},
	"recv_cipher": {
		"key": "",
		"nonce": 0
	},
	"rekey": {
		"interval": 10000,
		"reset_nonce": true
	}
}
Performing handshakes

The following code gives an example for how two participants, Alice and Bob, performs a handshake using the handshake pattern NN.

// Pattern used here is NN,
// -> e,
// <- e, ee

// alice is the initiator
alice, _ := babble.NewProtocol("Noise_NN_25519_ChaChaPoly_BLAKE2s", "Demo", true)
// bob is the responder
bob, _ := babble.NewProtocol("Noise_NN_25519_ChaChaPoly_BLAKE2s", "Demo", false)

// alice writes the first message, -> e
ciphertext, err := alice.WriteMessage(nil)
if err != nil {
    fmt.Println("alice: -> e, gives an error", err)
}
// bob reads the first message, ->
_, err = bob.ReadMessage(ciphertext)
if err != nil {
    fmt.Println("bob: -> e, gives an error", err)
}

// bob writes the second message, <- e, ee
ciphertext, err = bob.WriteMessage(nil)
if err != nil {
    fmt.Println("bob: <- e, ee, gives an error", err)
}
// alice reads the second message, <- e, ee
_, err = alice.ReadMessage(ciphertext)
if err != nil {
    fmt.Println("alice: <- e, ee, gives an error", err)
}

// the handshake is finished, we can verify that,
fmt.Println("alice's handshake is finished: ", alice.Finished())
fmt.Println("bob's handshake is finished: ", bob.Finished())

The full example can be found at examples/handshake.

Extentable Components

Aside from the built-in components, it's pretty straightforward to add new components to the framework using the Register method defined in each component's package. For instance, to add a new pattern,

import "github.com/crypto-y/babble/pattern"

// Register a dumb pattern
name := "YY"
rawPattern := `
  -> e
  <- e, ee, es`

// Register will validate the pattern, if invalid, an error is returned.
_ := pattern.Register(name, rawPattern)

// Now "YY" is a valid pattern name, and it can be used in the protocol name as,
p, _ := babble.NewProtocol("Noise_YY_25519_ChaChaPoly_BLAKE2s", "Demo", true)

You can check the package documentation for details on how to implement new components.

  • Cipher. To add a new cipher, implement the AEAD interface.
  • Hash. To add a new hash, implement the Hash interface.
  • DH. To add a new DHKE, implement the Curve interface.
  • Pattern. To add a new pattern, simply provide the pattern in string.

Vector tests

See vector documentation for more details.

Documentation

Overview

Package babble implements the Noise Protocol Framework.

https://noiseprotocol.org/

Supported patterns:

3 oneway patterns, 12 interactive patterns and 23 deffered patterns, with
PSK mode supported.

Supported dh curves:

curve448, curve25519 and secp256k1

Supported ciphers:

ChaCha20-Poly1305 and AESGCM

Supported hash functions:

SHA256, SHA512, BLAKE2b and BLAKE2s

Index

Constants

View Source
const CipherKeySize = 32

CipherKeySize defines the byte length of the key used for cipher.

View Source
const (
	// NoisePrefix is the mandatory prefix defined by the noise protocol framework.
	NoisePrefix = "Noise"
)

Variables

View Source
var (
	// ZEROS is a 32-byte array filled with zeros.
	ZEROS [CipherKeySize]byte

	// ZEROLEN is a zero-length byte sequence.
	ZEROLEN []byte
)
View Source
var (
	// ErrInvalidRekeyInterval is returned when interval is 0.
	ErrInvalidRekeyInterval = errors.New("rekey interval cannot be 0")

	// ErrMissingConfig is returned when no config file is provided.
	ErrMissingConfig = errors.New("missing config")

	// ErrProtocolInvalidName is returned when protocol name is wrong.
	ErrProtocolInvalidName = errors.New("invalid protocol name")
)

Functions

This section is empty.

Types

type CipherState

type CipherState struct {
	// Rekeyer is a customized rekey function.
	RekeyManger rekey.Rekeyer
	// contains filtered or unexported fields
}

CipherState contains key and nonce variables, which it uses to encrypt and decrypt ciphertext. During the handshake phase each party has a single CipherState, but during the transport phase each party has two CipherState instances, one for sending, and one for receiving.

func (*CipherState) DecryptWithAd

func (cs *CipherState) DecryptWithAd(ad, ciphertext []byte) ([]byte, error)

DecryptWithAd decrypts ciphertext with ad. If the key is non-empty it returns the decrypted plaintext, otherwise returns ciphertext.

If an authentication failure occurs in decryption then nonce is not incremented and an error is signaled to the caller.

func (*CipherState) EncryptWithAd

func (cs *CipherState) EncryptWithAd(ad, plaintext []byte) ([]byte, error)

EncryptWithAd encrypts plaintext with ad. If the key is non-empty it returns the encrypted ciphertext, otherwise returns plaintext.

func (*CipherState) Nonce

func (cs *CipherState) Nonce() uint64

Nonce returns the current nonce value.

func (*CipherState) Rekey

func (cs *CipherState) Rekey() error

Rekey updates the underlying cipher with a new key. If a rekeyer is defined for the Cipherstate, it's used to generate the new key. Otherwise, it uses the Rekey from the underlying cipher to generate a new key.

There are actually two places to customize a Rekey function. First here, then there's an opportunity in the underlying cipher.Rekey(). Also note that Rekey only updates the cipher's key value, it doesn't reset the cipher's nonce value, so applications performing Rekey must still perform a new handshake if sending 2^64 or more transport messages.

func (*CipherState) Reset

func (cs *CipherState) Reset()

Reset sets the cipher key to ZEROS, nonce to 0, and calls cipher.Reset.

func (*CipherState) SetNonce

func (cs *CipherState) SetNonce(n uint64)

SetNonce sets the nonce. This function is used for handling out-of-order transport messages

type DefaultRekeyerConfig

type DefaultRekeyerConfig struct {
	// Interval specifies the number of messages to be sent before a rekey is
	// performed.
	Interval uint64

	// ResetNonce decides whether to reset the cipher nonce to zero when a rekey
	// is performed.
	ResetNonce bool
}

DefaultRekeyerConfig is used for creating the default rekey manager.

type HandshakeState

type HandshakeState struct {

	// SendCipherState is used for encrypting plaintext once the handshake is
	// finished.
	SendCipherState *CipherState

	// RecvCipherState is used for decrypting ciphertext once the handshake is
	// finished.
	RecvCipherState *CipherState
	// contains filtered or unexported fields
}

HandshakeState object contains a symmetricState plus DH variables (s, e, rs, re) and a variable representing the handshake pattern. During the handshake phase each party has a single HandshakeState, which can be deleted once the handshake is finished.

func NewProtocol

func NewProtocol(name, prologue string,
	initiator bool) (*HandshakeState, error)

NewProtocol creates a new handshake state with the specified name prologue, and initiator. It calls the NewProtocolWithConfig with a default config, in which,

  • a default rekeyer is used, which resets the cipher key with an interval of 10000 and resets the nonce to be zero.

  • if any local ephemeral/static or remote ephemeral/static keys are needed by the message pattern prior to the creation of the handshake state, it will create the corresponding keys automatically.

NewProtocl doesn't support PSK mode, or specifying remote public keys prior to the creation of the handshake state, if needed, please use NewProtocolWithConfig instead.

func NewProtocolWithConfig

func NewProtocolWithConfig(config *ProtocolConfig) (*HandshakeState, error)

NewProtocolWithConfig creates a handshake state with parameters from a ProtocolConfig.

func (*HandshakeState) Finished

func (hs *HandshakeState) Finished() bool

Finished returns a bool to indicate whether the handshake is done. The patternIndex is used to track the index of last processed pattern, when it reaches the end, it indicates the patterns have all been processed.

func (*HandshakeState) GetChainingKey

func (hs *HandshakeState) GetChainingKey() []byte

GetChainingKey returns the chaining key in use.

func (*HandshakeState) GetDigest

func (hs *HandshakeState) GetDigest() []byte

GetDigest returns the hash digest in use.

func (*HandshakeState) GetInfo

func (hs *HandshakeState) GetInfo() ([]byte, error)

GetInfo gives a full picture of the handshake internal state.

func (*HandshakeState) ReadMessage

func (hs *HandshakeState) ReadMessage(message []byte) ([]byte, error)

ReadMessage takes a byte sequence containing a Noise handshake message, and return the decrypted message plaintext.

func (*HandshakeState) Reset

func (hs *HandshakeState) Reset()

Reset sets the handshake to initial state.

func (*HandshakeState) WriteMessage

func (hs *HandshakeState) WriteMessage(payload []byte) ([]byte, error)

WriteMessage takes a payload byte sequence which may be zero-length, and returns the ciphertext.

type ProtocolConfig

type ProtocolConfig struct {
	// Name is the protocol name defined by the noise specs, e.g.,
	// Noise_XX_25519_AESGCM_SHA256
	Name string

	// Initiator specifies whether it's the handshake initiator
	Initiator bool

	// Prologue is an optional information to be used when creating the
	// handskake state. Both parties must provide identical prologue data,
	// otherwisethe handshake will fail due to a decryption error.
	Prologue string

	// RekeyerConfig is a config used for set up the default rekeyer. If Rekeyer
	// is set, this variable is ignored.
	RekeyerConfig *DefaultRekeyerConfig

	// Rekeyer is a rekey manager, which controls when/how a rekey should be
	// performed, and whether the cipher nonce should be reset.
	Rekeyer rekey.Rekeyer

	// LocalStaticPriv is the s from the noise spec. Only provide it when it's
	// needed by the message pattern, otherwise leave it empty.
	LocalStaticPriv []byte

	// LocalEphemeralPriv is the e from the noise spec. Only provide it when
	// it's needed by the message pattern, otherwise leave it empty.
	LocalEphemeralPriv []byte

	// RemoteStaticPub is the rs from the noise spec. Only provide it when it's
	// needed by the message pattern, otherwise leave it empty.
	RemoteStaticPub []byte

	// RemoteEphemeralPub is the re from the noise spec. Only provide it when
	// it's needed by the message pattern, otherwise leave it empty.
	RemoteEphemeralPub []byte

	// Psks is used to store the pre-shared symmetric keys used if both parties
	// have a 32-byte shared secret keys.
	Psks [][]byte
	// contains filtered or unexported fields
}

ProtocolConfig is used for constructing a new handshake state.

Directories

Path Synopsis
Package cipher implements the cipher functions specified in the noise protocol.
Package cipher implements the cipher functions specified in the noise protocol.
Package dh implements the DH functions specified in the noise protocol.
Package dh implements the DH functions specified in the noise protocol.
examples
babble
This is an implemention for demonstration only.
This is an implemention for demonstration only.
handshake
This is an implemention for demonstration only.
This is an implemention for demonstration only.
newcipher
This is an implemention for demonstration only.
This is an implemention for demonstration only.
newdh
This is an implemention for demonstration only.
This is an implemention for demonstration only.
newhash
This is an implemention for demonstration only.
This is an implemention for demonstration only.
newpattern
This is an implemention for demonstration only.
This is an implemention for demonstration only.
Package hash implements the hash functions specified in the noise protocol.
Package hash implements the hash functions specified in the noise protocol.
Package pattern implements the noise handshake pattern.
Package pattern implements the noise handshake pattern.
Package rekey defines the rekey functions to be used in the babbel package.
Package rekey defines the rekey functions to be used in the babbel package.
Package vectors is created to help the vector test.
Package vectors is created to help the vector test.

Jump to

Keyboard shortcuts

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