scp

package
v0.0.0-...-367337e Latest Latest
Warning

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

Go to latest
Published: Jul 22, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package spc implements the Secure Channel Protocol, a custom TLS-inspired, check the github for more info.

Index

Constants

View Source
const (
	ErrUnknown        byte = 0x00 // unspecified error
	ErrInvalidPSK     byte = 0x01 // PSK proof verification failed
	ErrHandshakeFail  byte = 0x02 // Done MAC verification failed
	ErrInvalidMessage byte = 0x03 // unexpected message type received
)

SCP error codes sent in MsgError packets

View Source
const (
	NonceSize     = 16 // random per handsake
	PublicKeySize = 32 // X25519 public key
	PSKProofSize  = 32 // HMAC-SHA256 output
)

Variables

This section is empty.

Functions

func ComputePSKProof

func ComputePSKProof(psk []byte, data ...[]byte) []byte

ComputePSKProof computes an HMAC-SHA256 over the data fields, keyed by the PSK used to prove PSK knowledge during the handshake without revealing the PSK itself

func Decrypt

func Decrypt(key []byte, nonce []byte, ciphertext []byte) ([]byte, error)

Decrypt decrypts ciphertext using ChaCha20-Poly1305 with the given key and nonce Returns an error if authentication fails, any tampering with ciphertext will cause the decryption to fail entirely

func DeriveSessionKey

func DeriveSessionKey(sharedSecret [32]byte, psk []byte, clientNonce []byte, serverNonce []byte) ([]byte, error)

DeriveSessionKey derives a 32-byte session key from ECDH shared secret using HKDF-SHA256 The PSK is used as HKDF salt and both nonces are included into the info field to bind the key to this specific session

func EncodeClientHello

func EncodeClientHello(p ClientHelloPayload) []byte

func EncodeDataPayload

func EncodeDataPayload(p DataPayload) []byte

func EncodeError

func EncodeError(p ErrorPayload) []byte

func EncodeServerHello

func EncodeServerHello(p ServerHelloPayload) []byte

func Encrypt

func Encrypt(key []byte, nonce []byte, plaintext []byte) ([]byte, error)

Encrypt encrypts plaintext using ChaCha20-Poly1305 with the given key and nonce The nonce must be 12 bytes as thats what ChaCha20 uses Returns ciphertext with a 16-byte Poly1305 auth tag appended

func GenerateKeypair

func GenerateKeypair() (publicKey [32]byte, privateKey [32]byte, err error)

GenerateKeypair generates a fresh pair of ephemeral X25519 keypair a new keypair should be generated for EVERY handshake

func RandomNonce

func RandomNonce(size int) ([]byte, error)

func SharedSecret

func SharedSecret(privateKey [32]byte, peerPublicKey [32]byte) ([32]byte, error)

SharedSecret computes the X25519 Diffie-Hellman shared secret from a local private key and a peer's public key both sides independently arrive at the same shared secret

func WritePacket

func WritePacket(w io.Writer, p Packet) error

WritePacket serializes p and writes it to w The format is: [1B type][4B big-endian length][NB payload]

Types

type ClientHelloPayload

type ClientHelloPayload struct {
	Nonce     [NonceSize]byte
	PublicKey [PublicKeySize]byte
	PSKProof  [PSKProofSize]byte
}

func DecodeClientHello

func DecodeClientHello(payload []byte) (ClientHelloPayload, error)

type Config

type Config struct {
	// PSK is the pre-shared key used for mutual authentication.
	// Both sides must use the same PSK.
	PSK []byte
}

Config holds the configuration for an SCP connection, well duh.

type DataPayload

type DataPayload struct {
	Nonce      [12]byte
	Ciphertext []byte
}

Data ============

func DecodeDataPayload

func DecodeDataPayload(payload []byte) (DataPayload, error)

type ErrorPayload

type ErrorPayload struct {
	Code    byte
	Message string
}

func DecodeError

func DecodeError(payload []byte) (ErrorPayload, error)

type Listener

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

Listener accepts incoming SCP connections on a TCP address

func Listen

func Listen(addr string, cfg *Config) (*Listener, error)

Listen creates a Listener on the given TCP address Each accepted connection will perform the SCP server handshake using the PSK in cfg before a Session is returned

func (*Listener) Accept

func (l *Listener) Accept() (*Session, error)

Accept waits for an incoming connection and perform the handshake returning an established Session or an error if the handshake fail

func (*Listener) Close

func (l *Listener) Close() error

Close stops the Listener from accepting new connection

type MessageType

type MessageType byte

MessageType identifies the type of an SCP packet

const (
	MsgClientHello MessageType = 0x01 // opens the handshake
	MsgServerHello MessageType = 0x02 // server response to ClientHello
	MsgDone        MessageType = 0x03 // handshake verification
	MsgData        MessageType = 0x04 // encrypted application data
	MsgError       MessageType = 0x05 // protocol error, terminates connection
)

SCP message types

type NonceCounter

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

NonceCounter generates monotonically increasing 12-byte nonces for use with ChaCha20-Poly1305 The counter is encoded as a big-endian uint64 in the last 8 bytes of the nonce NOT safe for concurrent use

func NewNonceCounter

func NewNonceCounter() *NonceCounter

NewNonceCounter returns a new NounceCounter starting at zero

func (*NonceCounter) Next

func (n *NonceCounter) Next() []byte

Next returns the next 12-byte nonce and increments the counter must not be called more than 2^64 times with the same key, but tbh no one is gonna try that lmao

type Packet

type Packet struct {
	Type    MessageType
	Payload []byte
}

Packet is the basic unit of the SCP wire format Every SCP message is framed as a 5-byte header (type + length) followed by a payload of exactly length bytes

func ReadPacket

func ReadPacket(r io.Reader) (Packet, error)

ReadPacket reads one packet from r and returns it Blocks until a complete packet is available or an error occurs

type ServerHelloPayload

type ServerHelloPayload struct {
	Nonce     [NonceSize]byte
	PublicKey [PublicKeySize]byte
	PSKProof  [PSKProofSize]byte
}

func DecodeServerHello

func DecodeServerHello(payload []byte) (ServerHelloPayload, error)

type Session

type Session struct {
	Conn         net.Conn
	SessionKey   []byte
	NonceCounter NonceCounter
}

Session represents an established SCP connection Use Send and Receive to exchange encrypted messages

func ClientHandshake

func ClientHandshake(conn net.Conn, psk []byte) (*Session, error)

func Dial

func Dial(addr string, cfg *Config) (*Session, error)

Dial connects to an SCP server at addr and performs the handshake Returns an established Session ready for sending and receiving data Handshake verifies mutual PSK knowledge before returning

func ServerHandshake

func ServerHandshake(conn net.Conn, psk []byte) (*Session, error)

func (*Session) Close

func (s *Session) Close() error

Close closes the underlying network connection

func (*Session) Receive

func (s *Session) Receive() ([]byte, error)

Receive reads the next MsgData packet from the connection and returns the decrypted plaintext Blocks until a message arrives or error occurs

func (*Session) Send

func (s *Session) Send(plaintext []byte) error

Send encrypts plaintext and writes it to the connection as a MsgData packet each call uses a unique nonce from an internal counter

Jump to

Keyboard shortcuts

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