handshake

package
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Apr 13, 2026 License: MIT Imports: 20 Imported by: 0

Documentation

Overview

Package handshake implements ZK-enhanced peer authentication and session management.

Index

Constants

View Source
const (
	// ProtocolID is the legacy protocol identifier (unsigned challenges).
	ProtocolID = "/lango/handshake/1.0.0"

	// ProtocolIDv11 is the signed-challenge protocol (v1.1).
	ProtocolIDv11 = "/lango/handshake/1.1.0"

	// ProtocolIDv12 is the PQ KEM handshake protocol (v1.2).
	ProtocolIDv12 = "/lango/handshake/1.2.0"
)

Protocol version constants for handshake negotiation.

View Source
const NonceSize = 32

NonceSize is the expected byte length of a nonce.

Variables

This section is empty.

Functions

func PreferredProtocols added in v0.7.0

func PreferredProtocols(kemEnabled bool) []string

PreferredProtocols returns the ordered list of protocol IDs for initiator stream negotiation. When kemEnabled is true, v1.2 is tried first. Returns []string since libp2p protocol.ID is a string alias.

func VerifySecp256k1Signature added in v0.7.0

func VerifySecp256k1Signature(pubkey, message, signature []byte) error

VerifySecp256k1Signature is the default verifier using secp256k1+keccak256. It hashes the message with Keccak256, recovers the public key from the 65-byte ECDSA signature (R+S+V), and compares with the claimed compressed key.

Types

type ApprovalFunc

type ApprovalFunc func(ctx context.Context, pending *PendingHandshake) (bool, error)

ApprovalFunc is called to request user approval for an incoming handshake. Uses the callback pattern to avoid import cycles with the approval package.

type BundleAttacher added in v0.7.0

type BundleAttacher interface {
	Bundle() *identity.IdentityBundle
}

BundleAttacher is an optional interface that Signers can implement to provide their IdentityBundle for inclusion in handshake messages.

type Challenge

type Challenge struct {
	Nonce              []byte                   `json:"nonce"`
	Timestamp          int64                    `json:"timestamp"`
	SenderDID          string                   `json:"senderDid"`
	PublicKey          []byte                   `json:"publicKey,omitempty"`          // v1.1: initiator's public key
	Signature          []byte                   `json:"signature,omitempty"`          // v1.1: signature over canonical payload
	SignatureAlgorithm string                   `json:"signatureAlgorithm,omitempty"` // algorithm (empty = secp256k1-keccak256)
	Bundle             *identity.IdentityBundle `json:"bundle,omitempty"`             // v2: initiator's identity bundle
	KEMPublicKey       []byte                   `json:"kemPublicKey,omitempty"`       // v1.2: ephemeral hybrid KEM public key
	KEMAlgorithm       string                   `json:"kemAlgorithm,omitempty"`       // v1.2: "X25519-MLKEM768"
}

Challenge is sent by the initiator to start the handshake.

type ChallengeResponse

type ChallengeResponse struct {
	Nonce              []byte                   `json:"nonce"`
	Signature          []byte                   `json:"signature,omitempty"`
	ZKProof            []byte                   `json:"zkProof,omitempty"`
	DID                string                   `json:"did"`
	PublicKey          []byte                   `json:"publicKey"`
	SignatureAlgorithm string                   `json:"signatureAlgorithm,omitempty"` // algorithm (empty = secp256k1-keccak256)
	Bundle             *identity.IdentityBundle `json:"bundle,omitempty"`             // v2: responder's identity bundle
	KEMCiphertext      []byte                   `json:"kemCiphertext,omitempty"`      // v1.2: KEM ciphertext
}

ChallengeResponse is the target's reply with proof of identity.

type Config

type Config struct {
	Signer                 Signer
	LegacySigner           Signer // v1 secp256k1 fallback (optional)
	Sessions               *SessionStore
	ApprovalFn             ApprovalFunc
	ZKProver               ZKProverFunc
	ZKVerifier             ZKVerifierFunc
	ZKEnabled              bool
	Timeout                time.Duration
	AutoApproveKnown       bool
	NonceCache             *NonceCache
	RequireSignedChallenge bool
	Verifiers              map[string]SignatureVerifyFunc // nil → default with secp256k1 + ed25519
	BundleCache            identity.BundleResolver        // optional: for caching received bundles
	DIDAlias               *identity.DIDAlias             // optional: v1/v2 DID alias for session continuity
	EnablePQKEM            bool                           // enable PQ KEM key exchange (default false)
	Logger                 *zap.SugaredLogger
}

Config configures the Handshaker.

type Handshaker

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

Handshaker manages peer authentication using wallet signatures or ZK proofs.

func NewHandshaker

func NewHandshaker(cfg Config) *Handshaker

NewHandshaker creates a new peer authenticator.

func (*Handshaker) HandleIncoming

func (h *Handshaker) HandleIncoming(ctx context.Context, s network.Stream) (*Session, error)

HandleIncoming processes an incoming handshake request.

func (*Handshaker) Initiate

func (h *Handshaker) Initiate(ctx context.Context, s network.Stream, localDID string) (*Session, error)

Initiate starts a handshake with a remote peer over the given stream.

func (*Handshaker) StreamHandler

func (h *Handshaker) StreamHandler() network.StreamHandler

StreamHandler returns a libp2p stream handler for incoming handshakes.

func (*Handshaker) StreamHandlerV11

func (h *Handshaker) StreamHandlerV11() network.StreamHandler

StreamHandlerV11 returns a libp2p stream handler for v1.1 (signed challenge) handshakes. Uses the same HandleIncoming logic since it handles both signed and unsigned challenges.

func (*Handshaker) StreamHandlerV12 added in v0.7.0

func (h *Handshaker) StreamHandlerV12() network.StreamHandler

StreamHandlerV12 returns a libp2p stream handler for v1.2 (PQ KEM) handshakes.

type InvalidationReason

type InvalidationReason string

InvalidationReason describes why a session was invalidated.

const (
	ReasonLogout           InvalidationReason = "logout"
	ReasonReputationDrop   InvalidationReason = "reputation_drop"
	ReasonRepeatedFailures InvalidationReason = "repeated_failures"
	ReasonManualRevoke     InvalidationReason = "manual_revoke"
	ReasonSecurityEvent    InvalidationReason = "security_event"
)

type InvalidationRecord

type InvalidationRecord struct {
	PeerDID       string             `json:"peerDid"`
	Reason        InvalidationReason `json:"reason"`
	InvalidatedAt time.Time          `json:"invalidatedAt"`
}

InvalidationRecord stores details about a session invalidation.

type NonceCache

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

NonceCache prevents nonce replay attacks by tracking recently seen nonces.

func NewNonceCache

func NewNonceCache(ttl time.Duration) *NonceCache

NewNonceCache creates a new NonceCache with the given TTL.

func (*NonceCache) CheckAndRecord

func (nc *NonceCache) CheckAndRecord(nonce []byte) bool

CheckAndRecord returns true if the nonce has NOT been seen before (first occurrence). Returns false if the nonce was already recorded (replay detected). The nonce parameter must be exactly 32 bytes.

func (*NonceCache) Cleanup

func (nc *NonceCache) Cleanup()

Cleanup removes expired entries older than TTL.

func (*NonceCache) Start

func (nc *NonceCache) Start()

Start begins periodic cleanup using a ticker goroutine.

func (*NonceCache) Stop

func (nc *NonceCache) Stop()

Stop halts the periodic cleanup goroutine.

type PendingHandshake

type PendingHandshake struct {
	PeerID     peer.ID   `json:"peerId"`
	PeerDID    string    `json:"peerDid"`
	RemoteAddr string    `json:"remoteAddr"`
	Timestamp  time.Time `json:"timestamp"`
}

PendingHandshake describes a handshake awaiting user approval.

type SecurityEventHandler

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

SecurityEventHandler tracks tool execution failures and reputation changes to auto-invalidate sessions when thresholds are exceeded.

func NewSecurityEventHandler

func NewSecurityEventHandler(
	sessions *SessionStore,
	maxFailures int,
	minTrustScore float64,
	logger *zap.SugaredLogger,
) *SecurityEventHandler

NewSecurityEventHandler creates a handler that auto-invalidates sessions after consecutive tool failures or reputation drops below the threshold.

func (*SecurityEventHandler) OnReputationChange

func (h *SecurityEventHandler) OnReputationChange(peerDID string, newScore float64)

OnReputationChange invalidates the peer's session if the new score drops below the minimum trust threshold.

func (*SecurityEventHandler) RecordToolFailure

func (h *SecurityEventHandler) RecordToolFailure(peerDID string)

RecordToolFailure increments the consecutive failure counter for the peer. When the counter reaches maxFailures, the session is auto-invalidated.

func (*SecurityEventHandler) RecordToolSuccess

func (h *SecurityEventHandler) RecordToolSuccess(peerDID string)

RecordToolSuccess resets the consecutive failure counter for the peer.

type Session

type Session struct {
	PeerDID           string             `json:"peerDid"`
	Token             string             `json:"token"`
	CreatedAt         time.Time          `json:"createdAt"`
	ExpiresAt         time.Time          `json:"expiresAt"`
	ZKVerified        bool               `json:"zkVerified"`
	Invalidated       bool               `json:"invalidated"`
	InvalidatedReason InvalidationReason `json:"invalidatedReason,omitempty"`
	EncryptionKey     []byte             `json:"-"`       // derived from KEM, never serialized
	KEMUsed           bool               `json:"kemUsed"` // true if PQ KEM key exchange succeeded
}

Session represents an authenticated peer session.

func (*Session) IsExpired

func (s *Session) IsExpired() bool

IsExpired reports whether the session has expired.

type SessionAck

type SessionAck struct {
	Token     string `json:"token"`
	ExpiresAt int64  `json:"expiresAt"`
}

SessionAck is sent by the initiator after verifying the response.

type SessionStore

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

SessionStore manages authenticated peer sessions with TTL eviction.

func NewSessionStore

func NewSessionStore(ttl time.Duration) (*SessionStore, error)

NewSessionStore creates a session store with the given TTL.

func (*SessionStore) ActiveSessions

func (s *SessionStore) ActiveSessions() []*Session

ActiveSessions returns all non-expired, non-invalidated sessions.

func (*SessionStore) Cleanup

func (s *SessionStore) Cleanup() int

Cleanup removes all expired and invalidated sessions.

func (*SessionStore) Create

func (s *SessionStore) Create(peerDID string, zkVerified bool) (*Session, error)

Create creates a new session for the given peer DID.

func (*SessionStore) Get

func (s *SessionStore) Get(peerDID string) *Session

Get returns the session for the given peer DID, or nil if not found/expired/invalidated.

func (*SessionStore) GetByToken added in v0.6.0

func (s *SessionStore) GetByToken(token string) (string, bool)

GetByToken looks up an active session by its token and returns the peer DID.

func (*SessionStore) Invalidate

func (s *SessionStore) Invalidate(peerDID string, reason InvalidationReason)

Invalidate marks a session as invalidated, removes it from active sessions, records the invalidation, and fires the onInvalidate callback.

func (*SessionStore) InvalidateAll

func (s *SessionStore) InvalidateAll(reason InvalidationReason)

InvalidateAll invalidates all active sessions with the given reason.

func (*SessionStore) InvalidateByCondition

func (s *SessionStore) InvalidateByCondition(reason InvalidationReason, predicate func(*Session) bool)

InvalidateByCondition invalidates sessions matching the predicate.

func (*SessionStore) InvalidationHistory

func (s *SessionStore) InvalidationHistory() []InvalidationRecord

InvalidationHistory returns all recorded invalidation events.

func (*SessionStore) Remove

func (s *SessionStore) Remove(peerDID string)

Remove deletes a session, zeroing any encryption key first.

func (*SessionStore) SetInvalidationCallback

func (s *SessionStore) SetInvalidationCallback(fn func(peerDID string, reason InvalidationReason))

SetInvalidationCallback sets a function to be called when a session is invalidated.

func (*SessionStore) Validate

func (s *SessionStore) Validate(peerDID, token string) bool

Validate checks if a session token is valid for the given peer DID.

type SignatureVerifyFunc added in v0.7.0

type SignatureVerifyFunc func(pubkey, message, signature []byte) error

SignatureVerifyFunc verifies a signature against a claimed public key. Used for both challenge and response signature verification. pubkey is the claimed public key, message is the raw message (verifier handles algorithm-specific hashing internally), and signature is the raw signature bytes.

type Signer added in v0.7.0

type Signer interface {
	SignMessage(ctx context.Context, message []byte) ([]byte, error)
	PublicKey(ctx context.Context) ([]byte, error)
	Algorithm() string
	DID(ctx context.Context) (string, error)
}

Signer is the minimal interface for identity signing operations. Implementations must declare their algorithm and DID via Algorithm() and DID().

type ZKProverFunc

type ZKProverFunc func(ctx context.Context, challenge []byte) ([]byte, error)

ZKProverFunc generates a ZK ownership proof for the given challenge.

type ZKVerifierFunc

type ZKVerifierFunc func(ctx context.Context, proof, challenge, publicKey []byte) (bool, error)

ZKVerifierFunc verifies a ZK ownership proof.

Jump to

Keyboard shortcuts

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