brontide

package
v0.4.0-rc2 Latest Latest
Warning

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

Go to latest
Published: Aug 4, 2026 License: ISC Imports: 14 Imported by: 0

Documentation

Overview

Package brontide implements Handshake's Brontide P2P transport primitives.

Index

Constants

View Source
const (
	// ProtocolName is hsd's Noise protocol name for Handshake Brontide.
	ProtocolName = "Noise_XK_secp256k1_ChaChaPoly_SHA256+SVDW_Squared"

	// Prologue is mixed into the Handshake Brontide handshake hash.
	Prologue = "hns"

	// RotationInterval is hsd's cipher key rotation interval. The cipher
	// rotates after this many successful encryptions or decryptions.
	RotationInterval = 1000
)
View Source
const (
	// HeaderSize is the size of an encrypted hsd Brontide frame header:
	// 4-byte little-endian payload length plus a 16-byte authentication tag.
	HeaderSize = 4 + tagSize

	// MaxMessageSize is hsd's MAX_MESSAGE plus the 9-byte Handshake packet
	// envelope that is encrypted inside Brontide frames.
	MaxMessageSize = 8*1000*1000 + 9
)
View Source
const (
	// ActOneSize is hsd's ACT_ONE_SIZE: a 64-byte uniform ephemeral key
	// encoding plus a 16-byte authentication tag.
	ActOneSize = UniformPublicKeySize + tagSize

	// ActTwoSize is hsd's ACT_TWO_SIZE: a 64-byte uniform ephemeral key
	// encoding plus a 16-byte authentication tag.
	ActTwoSize = UniformPublicKeySize + tagSize

	// ActThreeSize is hsd's ACT_THREE_SIZE: a 33-byte encrypted static key
	// plus two 16-byte authentication tags.
	ActThreeSize = PublicKeySize + tagSize + tagSize

	// HandshakeTimeout bounds a complete act exchange, matching hsd's
	// Peer.CONNECT_TIMEOUT, which covers the Brontide handshake before the
	// stream emits its connect event.
	HandshakeTimeout = 5 * time.Second
)

hsd's Noise_XK three-act message pattern:

Act One   (initiator -> responder): e, es
Act Two   (responder -> initiator): e, ee
Act Three (initiator -> responder): s, se

Unlike LND's Brontide, hsd's acts carry no version byte: the ephemeral keys in acts one and two are sent as 64-byte Elligator Squared encodings, keeping the entire handshake indistinguishable from random bytes.

View Source
const (
	// PrivateKeySize is the size of a serialized secp256k1 private key.
	PrivateKeySize = btcec.PrivKeyBytesLen

	// PublicKeySize is the size of a compressed secp256k1 public key.
	PublicKeySize = btcec.PubKeyBytesLenCompressed
)
View Source
const (
	// IdentityKeyFile is the default file name for a Brontide node identity key.
	IdentityKeyFile = "brontide.key"
)
View Source
const (
	// UniformPublicKeySize is the size of an Elligator Squared encoded
	// secp256k1 point: two big-endian field elements.
	UniformPublicKeySize = 64
)

Variables

View Source
var (
	// ErrInvalidCipher is returned when a Brontide cipher state is nil.
	ErrInvalidCipher = errors.New("brontide: invalid cipher")

	// ErrInvalidKeySize is returned when a cipher key or salt is not 32 bytes.
	ErrInvalidKeySize = errors.New("brontide: invalid key size")

	// ErrDecrypt is returned when ChaCha20-Poly1305 authentication fails.
	ErrDecrypt = errors.New("brontide: decrypt failed")
)
View Source
var (
	// ErrInvalidPoint is returned when a secp256k1 point cannot be encoded
	// to, or decoded from, its Elligator Squared representation. It matches
	// bcrypto's 'Invalid point.' error.
	ErrInvalidPoint = errors.New("brontide: invalid point")

	// ErrInvalidUniformSize is returned when an Elligator Squared encoding
	// is not exactly 64 bytes. It matches bcrypto's 'Invalid hash size.'.
	ErrInvalidUniformSize = errors.New("brontide: invalid uniform key size")
)
View Source
var (
	// ErrFrameTooLarge is returned when a Brontide frame payload exceeds
	// hsd's maximum encrypted message size.
	ErrFrameTooLarge = errors.New("brontide: frame too large")

	// ErrShortFrame is returned when a complete-frame helper receives too
	// few bytes to contain a valid Brontide frame.
	ErrShortFrame = errors.New("brontide: short frame")

	// ErrFrameSizeMismatch is returned when the authenticated frame length
	// does not match the bytes supplied to a complete-frame helper.
	ErrFrameSizeMismatch = errors.New("brontide: frame size mismatch")
)
View Source
var (
	// ErrActSize is returned when a handshake act has the wrong length,
	// matching hsd's 'Act N: bad size.' errors.
	ErrActSize = errors.New("brontide: bad act size")

	// ErrActTag is returned when a handshake act fails authentication,
	// matching hsd's 'Act N: bad tag.' errors.
	ErrActTag = errors.New("brontide: bad act tag")

	// ErrHandshakeState is returned when handshake state is missing for the
	// requested act, such as an initiator without a remote static key.
	ErrHandshakeState = errors.New("brontide: invalid handshake state")
)
View Source
var (
	// ErrInvalidIdentityPath is returned when an identity-key path is empty.
	ErrInvalidIdentityPath = errors.New("brontide: invalid identity path")

	// ErrInvalidIdentityKey is returned when stored identity-key bytes are
	// malformed or outside the secp256k1 private key range.
	ErrInvalidIdentityKey = errors.New("brontide: invalid identity key")

	// ErrInsecureIdentityKeyPermissions is returned when a stored identity
	// key is readable or writable by group or other users.
	ErrInsecureIdentityKeyPermissions = errors.New(
		"brontide: insecure identity key permissions",
	)
)
View Source
var (
	// ErrInvalidConn is returned when a Brontide connection wrapper receives
	// a nil connection.
	ErrInvalidConn = errors.New("brontide: invalid connection")
)
View Source
var (
	// ErrInvalidKey is returned when Brontide key material is nil or invalid.
	ErrInvalidKey = errors.New("brontide: invalid key")
)

Functions

func ECDH

func ECDH(pub *btcec.PublicKey, priv *btcec.PrivateKey) ([keySize]byte, error)

ECDH returns hsd's Brontide ECDH secret: SHA256(compressed shared point).

func ECDHBytes

func ECDHBytes(pub []byte, priv *btcec.PrivateKey) ([keySize]byte, error)

ECDHBytes parses pub and returns hsd's Brontide ECDH secret: SHA256(compressed shared point).

func GenerateKey

func GenerateKey() (*btcec.PrivateKey, error)

GenerateKey creates a secp256k1 private key suitable for Brontide static or ephemeral key material.

func IdentityKeyPath

func IdentityKeyPath(dataDir string) string

IdentityKeyPath returns the default Brontide identity-key path under dataDir.

func IdentityStaticKey

func IdentityStaticKey(priv *btcec.PrivateKey) ([]byte, error)

IdentityStaticKey returns the compressed secp256k1 public key advertised as the node's Brontide static identity key.

func LoadIdentityKey

func LoadIdentityKey(path string) (*btcec.PrivateKey, error)

LoadIdentityKey loads a serialized secp256k1 Brontide identity key from path.

func LoadOrCreateIdentityKey

func LoadOrCreateIdentityKey(path string) (*btcec.PrivateKey, bool, error)

LoadOrCreateIdentityKey loads the node Brontide identity key at path, or generates and persists a new key when none exists. The returned bool is true when a new key was created.

func ParsePublicKey

func ParsePublicKey(serialized []byte) (*btcec.PublicKey, error)

ParsePublicKey parses a compressed secp256k1 public key.

func PublicKeyBytes

func PublicKeyBytes(priv *btcec.PrivateKey) []byte

PublicKeyBytes returns the compressed public key for priv.

func PublicKeyFromHash

func PublicKeyFromHash(uniform []byte) (*btcec.PublicKey, error)

PublicKeyFromHash decodes a 64-byte Elligator Squared encoding into a secp256k1 public key, matching bcrypto's publicKeyFromHash. Every 64-byte string decodes to a valid curve point except the negligible case where the two mapped points sum to infinity.

func PublicKeyToHash

func PublicKeyToHash(pub *btcec.PublicKey, rng io.Reader) ([]byte, error)

PublicKeyToHash encodes a secp256k1 public key as 64 bytes that are indistinguishable from uniformly random bytes, matching bcrypto's publicKeyToHash. The encoding is randomized: rng supplies the random field elements and preimage hints, and defaults to crypto/rand when nil. The returned bytes decode back to pub via PublicKeyFromHash.

func ReadFrame

func ReadFrame(recv *CipherState, frame []byte) ([]byte, error)

ReadFrame decrypts a complete hsd Brontide stream frame and returns its payload. A frame consumes two cipher nonces when both the header and payload authenticate successfully.

func SaveIdentityKey

func SaveIdentityKey(path string, priv *btcec.PrivateKey) error

SaveIdentityKey writes a Brontide identity key to path using owner-only file permissions. Existing files are replaced.

func WriteFrame

func WriteFrame(send *CipherState, payload []byte) ([]byte, error)

WriteFrame encrypts payload into one hsd Brontide stream frame. A frame consumes two cipher nonces: one for the length header and one for the body.

Types

type CipherState

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

CipherState is hsd's Brontide ChaCha20-Poly1305 cipher state. It keeps a key, a salt used for key rotation, and a monotonically increasing nonce.

func NewCipherState

func NewCipherState() *CipherState

NewCipherState returns a cipher initialized with the all-zero key and salt, matching hsd's initial Brontide cipher state.

func (*CipherState) Decrypt

func (c *CipherState) Decrypt(ciphertext, tag, ad []byte) ([]byte, error)

Decrypt authenticates and decrypts ciphertext with a detached tag and optional associated data.

func (*CipherState) Encrypt

func (c *CipherState) Encrypt(plaintext, ad []byte) ([]byte, []byte, error)

Encrypt encrypts plaintext with optional associated data. It returns the ciphertext and detached authentication tag.

func (*CipherState) InitKey

func (c *CipherState) InitKey(key []byte) error

InitKey replaces the cipher key and resets the nonce.

func (*CipherState) InitSalt

func (c *CipherState) InitSalt(key, salt []byte) error

InitSalt replaces the cipher salt, then initializes the key.

func (*CipherState) Nonce

func (c *CipherState) Nonce() uint32

Nonce returns the current message nonce. It is exposed for tests and for future transport diagnostics.

type Conn

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

Conn wraps a net.Conn with hsd-compatible Brontide frame encryption. It expects the Noise handshake to have already derived send and receive ciphers.

func ClientHandshake

func ClientHandshake(conn net.Conn, localPriv *btcec.PrivateKey,
	remotePub []byte) (*Conn, error)

ClientHandshake runs the initiator side of the Brontide handshake over conn and returns the encrypted connection. remotePub is the responder's compressed static public key. The exchange is bounded by HandshakeTimeout.

func ClientHandshakeTimeout

func ClientHandshakeTimeout(conn net.Conn, localPriv *btcec.PrivateKey,
	remotePub []byte, timeout time.Duration) (*Conn, error)

ClientHandshakeTimeout is ClientHandshake with a caller-selected timeout covering the complete act exchange.

func NewConn

func NewConn(conn net.Conn, send, recv *CipherState) (*Conn, error)

NewConn returns a Brontide-encrypted connection using established ciphers.

func ServerHandshake

func ServerHandshake(conn net.Conn,
	localPriv *btcec.PrivateKey) (*Conn, *btcec.PublicKey, error)

ServerHandshake runs the responder side of the Brontide handshake over conn and returns the encrypted connection along with the initiator's authenticated static public key. The exchange is bounded by HandshakeTimeout.

func ServerHandshakeTimeout

func ServerHandshakeTimeout(conn net.Conn, localPriv *btcec.PrivateKey,
	timeout time.Duration) (*Conn, *btcec.PublicKey, error)

ServerHandshakeTimeout is ServerHandshake with a caller-selected timeout covering the complete act exchange.

func (*Conn) Close

func (c *Conn) Close() error

Close closes the underlying connection.

func (*Conn) LocalAddr

func (c *Conn) LocalAddr() net.Addr

LocalAddr returns the local network address.

func (*Conn) Read

func (c *Conn) Read(p []byte) (int, error)

Read decrypts bytes from the Brontide stream. Frame boundaries are hidden from callers; unread bytes from a decrypted frame are buffered.

func (*Conn) RemoteAddr

func (c *Conn) RemoteAddr() net.Addr

RemoteAddr returns the remote network address.

func (*Conn) SetDeadline

func (c *Conn) SetDeadline(t time.Time) error

SetDeadline sets both read and write deadlines on the underlying connection.

func (*Conn) SetReadDeadline

func (c *Conn) SetReadDeadline(t time.Time) error

SetReadDeadline sets the read deadline on the underlying connection.

func (*Conn) SetWriteDeadline

func (c *Conn) SetWriteDeadline(t time.Time) error

SetWriteDeadline sets the write deadline on the underlying connection.

func (*Conn) Write

func (c *Conn) Write(p []byte) (int, error)

Write encrypts p into a single Brontide frame and writes it to the underlying connection.

type HandshakeState

type HandshakeState struct {
	SymmetricState
	// contains filtered or unexported fields
}

HandshakeState is hsd's Brontide handshake state: a Noise symmetric state plus the key material accumulated across the three Noise_XK acts.

func NewHandshakeState

func NewHandshakeState(initiator bool, localPriv *btcec.PrivateKey,
	remotePub *btcec.PublicKey) (*HandshakeState, error)

NewHandshakeState initializes Brontide handshake state for one side of a connection, mirroring hsd's HandshakeState.initState. Initiators must supply the responder's static public key; responders pass nil.

func (*HandshakeState) GenActOne

func (hs *HandshakeState) GenActOne() ([ActOneSize]byte, error)

GenActOne generates act one for the initiator: a uniform-encoded ephemeral key and a tag binding the es ECDH result.

func (*HandshakeState) GenActThree

func (hs *HandshakeState) GenActThree() ([ActThreeSize]byte, error)

GenActThree generates act three for the initiator: the encrypted local static key and a tag binding the se ECDH result. It derives the final transport ciphers.

func (*HandshakeState) GenActTwo

func (hs *HandshakeState) GenActTwo() ([ActTwoSize]byte, error)

GenActTwo generates act two for the responder: a uniform-encoded ephemeral key and a tag binding the ee ECDH result.

func (*HandshakeState) RecvActOne

func (hs *HandshakeState) RecvActOne(actOne []byte) error

RecvActOne processes act one on the responder.

func (*HandshakeState) RecvActThree

func (hs *HandshakeState) RecvActThree(actThree []byte) error

RecvActThree processes act three on the responder, authenticating the initiator's static key and deriving the final transport ciphers.

func (*HandshakeState) RecvActTwo

func (hs *HandshakeState) RecvActTwo(actTwo []byte) error

RecvActTwo processes act two on the initiator.

func (*HandshakeState) RemoteStatic

func (hs *HandshakeState) RemoteStatic() *btcec.PublicKey

RemoteStatic returns the remote static public key: the key supplied at initialization for initiators, or the key learned in act three for responders.

type SymmetricState

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

SymmetricState is the Noise symmetric state used by Handshake Brontide.

func NewSymmetricState

func NewSymmetricState() *SymmetricState

NewSymmetricState initializes a symmetric state with hsd's protocol name.

func (*SymmetricState) ChainKey

func (s *SymmetricState) ChainKey() [keySize]byte

ChainKey returns the current chaining key.

func (*SymmetricState) DecryptHash

func (s *SymmetricState) DecryptHash(ciphertext, tag []byte) ([]byte, error)

DecryptHash authenticates and decrypts ciphertext with the current digest as associated data, then mixes the ciphertext and tag into the digest.

func (*SymmetricState) Digest

func (s *SymmetricState) Digest() [keySize]byte

Digest returns the current handshake digest.

func (*SymmetricState) EncryptHash

func (s *SymmetricState) EncryptHash(plaintext []byte) ([]byte, []byte, error)

EncryptHash encrypts plaintext with the current digest as associated data and mixes the resulting ciphertext and tag into the digest.

func (*SymmetricState) InitSymmetric

func (s *SymmetricState) InitSymmetric(protocolName string)

InitSymmetric initializes the handshake digest, chaining key, and zero-key cipher state for a Noise protocol name.

func (*SymmetricState) MixHash

func (s *SymmetricState) MixHash(parts ...[]byte)

MixHash updates the handshake digest with one or more byte slices.

func (*SymmetricState) MixKey

func (s *SymmetricState) MixKey(input []byte)

MixKey mixes input key material into the chaining key and reinitializes the handshake cipher with the derived temporary key.

func (*SymmetricState) Split

func (s *SymmetricState) Split(initiator bool) (*CipherState, *CipherState, error)

Split derives send and receive ciphers from the final chaining key. The initiator uses the first derived key for sending; the responder uses it for receiving, matching hsd.

Jump to

Keyboard shortcuts

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