go-crypt/specs/auth
2026-03-27 21:45:33 +00:00
..
README.md docs: populate package specs 2026-03-27 19:30:16 +00:00
RFC.md Add RFC specs for auth crypt and trust packages 2026-03-27 21:45:33 +00:00

auth

Import: dappco.re/go/core/crypt/auth

Files: 4

Types

Authenticator

type Authenticator struct {
	medium		io.Medium
	store		SessionStore
	hardwareKey	HardwareKey		// optional hardware key (nil = software only)
	challenges	map[string]*Challenge	// userID -> pending challenge
	mu		sync.RWMutex		// protects challenges map only
	challengeTTL	time.Duration
	sessionTTL	time.Duration
}

Authenticator manages PGP-based challenge-response authentication. All user data and keys are persisted through an io.Medium, which may be backed by disk, memory (MockMedium), or any other storage backend. Sessions are persisted via a SessionStore (in-memory by default, optionally SQLite-backed for crash recovery).

An optional HardwareKey can be provided via WithHardwareKey for hardware-backed cryptographic operations (PKCS#11, YubiKey, etc.). See auth/hardware.go for the interface definition and integration points. Usage: create an Authenticator with New(...) and then call Register, Login, or CreateChallenge.

Methods

CreateChallenge
func (a *Authenticator) CreateChallenge(userID string) (*Challenge, error)

CreateChallenge generates a cryptographic challenge for the given user. A random nonce is created and encrypted with the user's PGP public key. The client must decrypt the nonce and sign it to prove key ownership. Usage: call CreateChallenge(...) during the package's normal workflow.

DeleteUser
func (a *Authenticator) DeleteUser(userID string) error

DeleteUser removes a user and all associated keys from storage. The "server" user is protected and cannot be deleted (mirroring the original TypeScript implementation's safeguard). Usage: call DeleteUser(...) during the package's normal workflow.

IsRevoked
func (a *Authenticator) IsRevoked(userID string) bool

IsRevoked checks whether a user's key has been revoked by inspecting the .rev file. Returns true only if the file contains valid revocation JSON (not the legacy "REVOCATION_PLACEHOLDER" string). Usage: call IsRevoked(...) during the package's normal workflow.

Login
func (a *Authenticator) Login(userID, password string) (*Session, error)

Login performs password-based authentication as a convenience method. It verifies the password against the stored hash and, on success, creates a new session. This bypasses the PGP challenge-response flow.

Hash format detection:

  • If a .hash file exists, its content starts with "$argon2id$" and is verified using constant-time Argon2id comparison.
  • Otherwise, falls back to legacy .lthn file with LTHN hash verification. On successful legacy login, the password is re-hashed with Argon2id and a .hash file is written (transparent migration).

Usage: call Login(...) for password-based flows when challenge-response is not required.

ReadResponseFile
func (a *Authenticator) ReadResponseFile(userID, path string) (*Session, error)

ReadResponseFile reads a signed response from a file and validates it, completing the air-gapped authentication flow. The file must contain the raw PGP signature bytes (armored). Usage: call ReadResponseFile(...) during the package's normal workflow.

RefreshSession
func (a *Authenticator) RefreshSession(token string) (*Session, error)

RefreshSession extends the expiry of an existing valid session. Usage: call RefreshSession(...) during the package's normal workflow.

Register
func (a *Authenticator) Register(username, password string) (*User, error)

Register creates a new user account. It hashes the username with LTHN to produce a userID, generates a PGP keypair (protected by the given password), and persists the public key, private key, revocation placeholder, password hash (Argon2id), and encrypted metadata via the Medium. Usage: call Register(...) during the package's normal workflow.

RevokeKey
func (a *Authenticator) RevokeKey(userID, password, reason string) error

RevokeKey marks a user's key as revoked. It verifies the password first, writes a JSON revocation record to the .rev file (replacing the placeholder), and invalidates all sessions for the user. Usage: call RevokeKey(...) during the package's normal workflow.

RevokeSession
func (a *Authenticator) RevokeSession(token string) error

RevokeSession removes a session, invalidating the token immediately. Usage: call RevokeSession(...) during the package's normal workflow.

RotateKeyPair
func (a *Authenticator) RotateKeyPair(userID, oldPassword, newPassword string) (*User, error)

RotateKeyPair generates a new PGP keypair for the given user, re-encrypts their metadata with the new key, updates the password hash, and invalidates all existing sessions. The caller must provide the current password (oldPassword) to decrypt existing metadata and the new password (newPassword) to protect the new keypair. Usage: call RotateKeyPair(...) during the package's normal workflow.

StartCleanup
func (a *Authenticator) StartCleanup(ctx context.Context, interval time.Duration)

StartCleanup runs a background goroutine that periodically removes expired sessions from the store. It stops when the context is cancelled. Usage: call StartCleanup(...) during the package's normal workflow.

ValidateResponse
func (a *Authenticator) ValidateResponse(userID string, signedNonce []byte) (*Session, error)

ValidateResponse verifies a signed nonce from the client. The client must have decrypted the challenge nonce and signed it with their private key. On success, a new session is created and returned. Usage: call ValidateResponse(...) during the package's normal workflow.

ValidateSession
func (a *Authenticator) ValidateSession(token string) (*Session, error)

ValidateSession checks whether a token maps to a valid, non-expired session. Usage: call ValidateSession(...) during the package's normal workflow.

WriteChallengeFile
func (a *Authenticator) WriteChallengeFile(userID, path string) error

WriteChallengeFile writes an encrypted challenge to a file for air-gapped (courier) transport. The challenge is created and then its encrypted nonce is written to the specified path on the Medium. Usage: call WriteChallengeFile(...) during the package's normal workflow.

Challenge

type Challenge struct {
	Nonce		[]byte		`json:"nonce"`
	Encrypted	string		`json:"encrypted"`	// PGP-encrypted nonce (armored)
	ExpiresAt	time.Time	`json:"expires_at"`
}

Challenge is a PGP-encrypted nonce sent to a client during authentication. Usage: use Challenge with the other exported helpers in this package.

HardwareKey

type HardwareKey interface {
	// Sign produces a cryptographic signature over the given data using the
	// hardware-stored private key. The signature format depends on the
	// underlying device (e.g. ECDSA, RSA-PSS, EdDSA).
	Sign(data []byte) ([]byte, error)

	// Decrypt decrypts ciphertext using the hardware-stored private key.
	// The ciphertext format must match what the device expects (e.g. RSA-OAEP).
	Decrypt(ciphertext []byte) ([]byte, error)

	// GetPublicKey returns the PEM or armored public key corresponding to the
	// hardware-stored private key.
	GetPublicKey() (string, error)

	// IsAvailable reports whether the hardware key device is currently
	// connected and operational. Callers should check this before attempting
	// Sign or Decrypt to provide graceful fallback behaviour.
	IsAvailable() bool
}

HardwareKey defines the contract for hardware-backed cryptographic operations. Implementations should wrap PKCS#11 tokens, YubiKeys, TPM modules, or similar tamper-resistant devices.

All methods must be safe for concurrent use. Usage: implement HardwareKey and pass it to WithHardwareKey(...) to wire hardware-backed auth into New(...).

MemorySessionStore

type MemorySessionStore struct {
	mu		sync.RWMutex
	sessions	map[string]*Session
}

MemorySessionStore is an in-memory SessionStore backed by a map. Usage: use MemorySessionStore with the other exported helpers in this package.

Methods

Cleanup
func (m *MemorySessionStore) Cleanup() (int, error)

Cleanup removes all expired sessions and returns the count removed. Usage: call Cleanup(...) during the package's normal workflow.

Delete
func (m *MemorySessionStore) Delete(token string) error

Delete removes a session by token. Usage: call Delete(...) during the package's normal workflow.

DeleteByUser
func (m *MemorySessionStore) DeleteByUser(userID string) error

DeleteByUser removes all sessions belonging to the given user. Usage: call DeleteByUser(...) during the package's normal workflow.

Get
func (m *MemorySessionStore) Get(token string) (*Session, error)

Get retrieves a session by token. Usage: call Get(...) during the package's normal workflow.

Set
func (m *MemorySessionStore) Set(session *Session) error

Set stores a session, keyed by its token. Usage: call Set(...) during the package's normal workflow.

Option

type Option func(*Authenticator)

Option configures an Authenticator. Usage: use Option with the other exported helpers in this package.

Revocation

type Revocation struct {
	UserID		string		`json:"user_id"`
	Reason		string		`json:"reason"`
	RevokedAt	time.Time	`json:"revoked_at"`
}

Revocation records the details of a revoked user key. Stored as JSON in the user's .rev file, replacing the legacy placeholder. Usage: use Revocation with the other exported helpers in this package.

SQLiteSessionStore

type SQLiteSessionStore struct {
	mu	sync.Mutex
	store	*store.Store
}

SQLiteSessionStore is a SessionStore backed by core/store (SQLite KV). A mutex serialises all operations because SQLite is single-writer. Usage: use SQLiteSessionStore with the other exported helpers in this package.

Methods

Cleanup
func (s *SQLiteSessionStore) Cleanup() (int, error)

Cleanup removes all expired sessions and returns the count removed. Usage: call Cleanup(...) during the package's normal workflow.

Close
func (s *SQLiteSessionStore) Close() error

Close closes the underlying SQLite store. Usage: call Close(...) during the package's normal workflow.

Delete
func (s *SQLiteSessionStore) Delete(token string) error

Delete removes a session by token from SQLite. Usage: call Delete(...) during the package's normal workflow.

DeleteByUser
func (s *SQLiteSessionStore) DeleteByUser(userID string) error

DeleteByUser removes all sessions belonging to the given user. Usage: call DeleteByUser(...) during the package's normal workflow.

Get
func (s *SQLiteSessionStore) Get(token string) (*Session, error)

Get retrieves a session by token from SQLite. Usage: call Get(...) during the package's normal workflow.

Set
func (s *SQLiteSessionStore) Set(session *Session) error

Set stores a session in SQLite, keyed by its token. Usage: call Set(...) during the package's normal workflow.

Session

type Session struct {
	Token		string		`json:"token"`
	UserID		string		`json:"user_id"`
	ExpiresAt	time.Time	`json:"expires_at"`
}

Session represents an authenticated session. Usage: use Session with the other exported helpers in this package.

SessionStore

type SessionStore interface {
	Get(token string) (*Session, error)
	Set(session *Session) error
	Delete(token string) error
	DeleteByUser(userID string) error
	Cleanup() (int, error)	// Remove expired sessions, return count removed
}

SessionStore abstracts session persistence. Usage: use SessionStore with the other exported helpers in this package.

User

type User struct {
	PublicKey	string		`json:"public_key"`
	KeyID		string		`json:"key_id"`
	Fingerprint	string		`json:"fingerprint"`
	PasswordHash	string		`json:"password_hash"`	// Argon2id (new) or LTHN (legacy)
	Created		time.Time	`json:"created"`
	LastLogin	time.Time	`json:"last_login"`
}

User represents a registered user with PGP credentials. Usage: use User with the other exported helpers in this package.

Functions

New

func New(m io.Medium, opts ...Option) *Authenticator

New creates an Authenticator that persists user data via the given Medium. By default, sessions are stored in memory. Use WithSessionStore to provide a persistent implementation (e.g. SQLiteSessionStore). Usage: call New(...) to create a ready-to-use value.

NewMemorySessionStore

func NewMemorySessionStore() *MemorySessionStore

NewMemorySessionStore creates a new in-memory session store. Usage: call NewMemorySessionStore(...) to create a ready-to-use value.

NewSQLiteSessionStore

func NewSQLiteSessionStore(dbPath string) (*SQLiteSessionStore, error)

NewSQLiteSessionStore creates a new SQLite-backed session store. Use ":memory:" for testing or a file path for persistent storage. Usage: call NewSQLiteSessionStore(...) to create a ready-to-use value.

WithChallengeTTL

func WithChallengeTTL(d time.Duration) Option

WithChallengeTTL sets the lifetime of a challenge before it expires. Usage: pass WithChallengeTTL(...) into the related constructor to adjust the default behaviour.

WithHardwareKey

func WithHardwareKey(hk HardwareKey) Option

WithHardwareKey configures the Authenticator to use a hardware key for cryptographic operations where supported. When set, the Authenticator may delegate signing, decryption, and public key retrieval to the hardware device instead of using software PGP keys.

This is a forward-looking option — integration points are documented in auth.go but not yet wired up. Usage: pass WithHardwareKey(...) into New(...) to enable a HardwareKey implementation.

WithSessionStore

func WithSessionStore(s SessionStore) Option

WithSessionStore sets the SessionStore implementation. If not provided, an in-memory store is used (sessions lost on restart). Usage: pass WithSessionStore(...) into the related constructor to adjust the default behaviour.

WithSessionTTL

func WithSessionTTL(d time.Duration) Option

WithSessionTTL sets the lifetime of a session before it expires. Usage: pass WithSessionTTL(...) into the related constructor to adjust the default behaviour.