Documentation
¶
Overview ¶
Package database provides a pure-Go SQLite-backed credential store.
Index ¶
- func Decrypt(key, ciphertext []byte) ([]byte, error)
- func DecryptEntry(masterKey, encryptedData, salt []byte) ([]byte, error)
- func DefaultDBPath() (string, error)
- func DeriveKey(password, salt []byte, params Argon2idParams) []byte
- func Encrypt(key, plaintext []byte) ([]byte, error)
- func EncryptEntry(masterKey, plaintext []byte) (encryptedData, salt []byte, err error)
- func GenerateEncryptionKey() ([]byte, error)
- func GenerateSalt(length int) ([]byte, error)
- type Argon2idParams
- type AuditEntry
- type EntryType
- type KeyMetadata
- type KeySource
- type KeychainSource
- type MasterPasswordSource
- type Option
- type PasswordEntry
- type PasswordPromptFunc
- type Store
- func (s *Store) Close() error
- func (s *Store) DeleteEntry(account, service string) error
- func (s *Store) GetActiveKeyMetadata() (*KeyMetadata, error)
- func (s *Store) GetMFASerialBytes(account, profile string) ([]byte, error)
- func (s *Store) GetSecret(account, service string) ([]byte, error)
- func (s *Store) GetSecretString(account, service string) (string, error)
- func (s *Store) InitKeyMetadata() error
- func (s *Store) ListEntries(service string) (_ []keychain.KeychainEntry, err error)
- func (s *Store) SearchEntries(query string) (_ []keychain.KeychainEntry, err error)
- func (s *Store) SetDescription(service, account, description string) error
- func (s *Store) SetDescriptionAt(service, account, description string, updatedAt time.Time) (err error)
- func (s *Store) SetSecret(account, service string, secret []byte) error
- func (s *Store) SetSecretAt(account, service string, secret []byte, createdAt, updatedAt time.Time) error
- func (s *Store) SetSecretString(account, service, secret string) error
- func (s *Store) StoreKeyMetadata(meta *KeyMetadata) error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Decrypt ¶
Decrypt decrypts ciphertext produced by Encrypt using AES-256-GCM. The key must be exactly 32 bytes.
func DecryptEntry ¶
DecryptEntry decrypts data produced by EncryptEntry.
func DefaultDBPath ¶
DefaultDBPath returns the platform-appropriate path for the sesh database.
- macOS: ~/Library/Application Support/sesh/passwords.db
- Linux: $XDG_DATA_HOME/sesh/passwords.db (falls back to ~/.local/share/sesh/passwords.db; a relative $XDG_DATA_HOME is ignored per the XDG Base Directory spec)
- Windows: %APPDATA%/sesh/passwords.db (falls back to ~/AppData/Roaming/sesh/passwords.db)
func DeriveKey ¶
func DeriveKey(password, salt []byte, params Argon2idParams) []byte
DeriveKey uses Argon2id to derive an encryption key from a password and salt.
func Encrypt ¶
Encrypt encrypts plaintext using AES-256-GCM with the provided key. The returned ciphertext is nonce || encrypted_data || tag. The key must be exactly 32 bytes; shorter keys are rejected rather than accepted as AES-128 or AES-192.
func EncryptEntry ¶
EncryptEntry encrypts plaintext for storage, generating a per-entry salt and deriving a per-entry key from the master key material + salt. Returns (encryptedData, salt, error).
func GenerateEncryptionKey ¶
GenerateEncryptionKey creates a new random 256-bit encryption key.
func GenerateSalt ¶
GenerateSalt produces a cryptographically random salt of the given length.
Types ¶
type Argon2idParams ¶
type Argon2idParams struct {
Time uint32 `json:"time"` // iterations
Memory uint32 `json:"memory"` // KiB
Threads uint8 `json:"threads"` // parallelism
KeyLen uint32 `json:"key_len"` // derived key length in bytes
}
Argon2idParams holds the tuning parameters for Argon2id key derivation.
func DefaultArgon2idParams ¶
func DefaultArgon2idParams() Argon2idParams
DefaultArgon2idParams returns production-grade Argon2id parameters. Time=3, Memory=64 MiB, Threads=4, KeyLen=32 (AES-256).
func UnmarshalArgon2idParams ¶
func UnmarshalArgon2idParams(data string) (Argon2idParams, error)
UnmarshalArgon2idParams deserialises Argon2id parameters from JSON.
func (Argon2idParams) MarshalParams ¶
func (p Argon2idParams) MarshalParams() string
MarshalParams serialises Argon2id parameters to JSON for storage in key_metadata. The struct is composed of fixed-width integers, so json.Marshal cannot fail for any valid Argon2idParams value — a non-nil error here indicates a programming bug (e.g. someone added an unmarshalable field).
type AuditEntry ¶
type AuditEntry struct {
CreatedAt time.Time
EventType string
EntryID string // nullable — empty for auth events
Detail string
ID int64
}
AuditEntry represents a row in the audit_log table.
type KeyMetadata ¶
type KeyMetadata struct {
CreatedAt time.Time
Algorithm string // "argon2id", "pbkdf2"
Params string // JSON: time, memory, threads (argon2id) or iterations (pbkdf2)
Salt []byte
Version int
Active bool
}
KeyMetadata stores key derivation parameters for a given key version. This table is readable without decryption so the store can derive the decryption key before reading any password entries.
type KeySource ¶
type KeySource interface {
// GetEncryptionKey returns the master encryption key.
// The caller must zero the returned slice after use.
GetEncryptionKey() ([]byte, error)
// StoreEncryptionKey persists a new master encryption key.
StoreEncryptionKey(key []byte) error
// RequiresUserInput reports whether retrieving the key
// needs interactive input (e.g. a master password prompt).
RequiresUserInput() bool
// Name returns a human-readable name for this key source.
Name() string
}
KeySource abstracts where the master encryption key comes from. The keychain source is implemented now; a master-password source can be added later without changing the store.
type KeychainSource ¶
type KeychainSource struct {
// contains filtered or unexported fields
}
KeychainSource retrieves and stores the master encryption key in the OS keychain.
func NewKeychainSource ¶
func NewKeychainSource(kc keychainKeyProvider, account string) *KeychainSource
NewKeychainSource creates a KeychainSource that stores the encryption key under the given account in the OS keychain.
func (*KeychainSource) GetEncryptionKey ¶
func (s *KeychainSource) GetEncryptionKey() ([]byte, error)
GetEncryptionKey reads the master key from the keychain. The stored value is a hex-encoded string of the raw 32-byte key. The keychain backend passes values through `security -i`, whose tokenizer splits on whitespace and control bytes — random binary keys regularly contain those bytes and fail to store, so we keep the at-rest form in ASCII-safe hex.
func (*KeychainSource) Name ¶
func (s *KeychainSource) Name() string
func (*KeychainSource) RequiresUserInput ¶
func (s *KeychainSource) RequiresUserInput() bool
func (*KeychainSource) StoreEncryptionKey ¶
func (s *KeychainSource) StoreEncryptionKey(key []byte) error
StoreEncryptionKey persists the master key in the keychain. The raw 32-byte key is hex-encoded before storage so the resulting string is guaranteed to contain only [0-9a-f] — safe for the keychain backend's `security -i` text protocol. See GetEncryptionKey for context.
type MasterPasswordSource ¶
type MasterPasswordSource struct {
// contains filtered or unexported fields
}
MasterPasswordSource derives the encryption key from a user-supplied passphrase via Argon2id. The KDF salt and a verification blob are stored in a sidecar file alongside the DB — no keychain involvement.
func NewMasterPasswordSource ¶
func NewMasterPasswordSource(dataDir string, prompt PasswordPromptFunc, opts ...Option) *MasterPasswordSource
NewMasterPasswordSource creates a MasterPasswordSource whose sidecar lives at <dataDir>/passwords.key — the canonical layout used by the CLI. Callers that need a non-canonical sidecar path (e.g., rotation staging) should use NewMasterPasswordSourceAtPath directly.
func NewMasterPasswordSourceAtPath ¶
func NewMasterPasswordSourceAtPath(sidecarPath string, prompt PasswordPromptFunc, opts ...Option) *MasterPasswordSource
NewMasterPasswordSourceAtPath creates a MasterPasswordSource that reads and writes its sidecar at the given absolute path. Useful when staging a rotation: the source instance can point at passwords.key while a target instance points at passwords.key.new, both running concurrently without colliding.
func (*MasterPasswordSource) Close ¶
func (s *MasterPasswordSource) Close()
Close zeroes and releases the cached key. Safe to call multiple times and safe to call concurrently with GetEncryptionKey. Bumping cacheEpoch fences any in-flight derivation from re-populating the cache after Close returns.
func (*MasterPasswordSource) GetEncryptionKey ¶
func (s *MasterPasswordSource) GetEncryptionKey() ([]byte, error)
GetEncryptionKey prompts for the master password and derives the encryption key. On first run (no sidecar), it prompts twice for confirmation and creates the sidecar. On subsequent runs, it verifies the password against the stored verification blob. The derived key is cached for the lifetime of this source so repeated Get/Set operations within one invocation do not re-prompt.
Per the KeySource contract the caller is free to zero the returned slice — the cache holds a private copy. Call Close() to clear the cached key when done.
func (*MasterPasswordSource) Name ¶
func (s *MasterPasswordSource) Name() string
func (*MasterPasswordSource) RequiresUserInput ¶
func (s *MasterPasswordSource) RequiresUserInput() bool
func (*MasterPasswordSource) StoreEncryptionKey ¶
func (s *MasterPasswordSource) StoreEncryptionKey(_ []byte) error
StoreEncryptionKey is a no-op for the master password source — the key is derived from the password, not stored directly.
type Option ¶
type Option func(*MasterPasswordSource)
Option configures a MasterPasswordSource. Use with NewMasterPasswordSource.
func WithMaxAttempts ¶
WithMaxAttempts sets the maximum number of password prompts the unlock loop will issue before giving up. Values < 1 are clamped to 1. Only wrong-password failures are retried; sidecar/IO errors fail immediately.
The prompt callback must produce fresh user input on each invocation — a callback that returns a constant value (e.g., one backed by an env var) will derive the same wrong key N times and waste ~N × Argon2id cycles before failing. The CLI gates this via resolvePasswordPrompt in main.go, which only marks a prompt interactive when it actually reads fresh bytes; direct callers must apply the same discipline.
Only affects unlock(); first-run create+confirm always runs once.
type PasswordEntry ¶
type PasswordEntry struct {
CreatedAt time.Time
UpdatedAt time.Time
ID string
Service string
Account string
EntryType EntryType
Metadata string
EncryptedData []byte
Salt []byte
KeyVersion int
}
PasswordEntry represents a row in the passwords table.
type PasswordPromptFunc ¶
PasswordPromptFunc is called to obtain the master password from the user. Implementations should not echo the input.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store is a SQLite-backed credential store that satisfies keychain.Provider.
func Open ¶
Open creates or opens the SQLite database at dbPath, runs any pending migrations, and returns a ready-to-use Store.
func (*Store) Close ¶
Close releases the database connection and clears any cached key material held by the key source.
func (*Store) DeleteEntry ¶
func (*Store) GetActiveKeyMetadata ¶
func (s *Store) GetActiveKeyMetadata() (*KeyMetadata, error)
GetActiveKeyMetadata returns the currently active key metadata.
func (*Store) GetMFASerialBytes ¶
func (*Store) GetSecretString ¶
func (*Store) InitKeyMetadata ¶
InitKeyMetadata creates the initial key metadata entry if none exists. Called during store initialisation when using a keychain key source.
func (*Store) ListEntries ¶
func (s *Store) ListEntries(service string) (_ []keychain.KeychainEntry, err error)
func (*Store) SearchEntries ¶
func (s *Store) SearchEntries(query string) (_ []keychain.KeychainEntry, err error)
SearchEntries performs a full-text search across service, account, and metadata using the FTS5 index. Returns matching KeychainEntry rows. An empty or whitespace-only query returns no results (FTS5 would reject it).
func (*Store) SetDescription ¶
func (*Store) SetDescriptionAt ¶
func (s *Store) SetDescriptionAt(service, account, description string, updatedAt time.Time) (err error)
SetDescriptionAt sets the description and stamps updated_at with the given timestamp. Zero falls back to the current time. Implements keychain.TimestampedStore.
func (*Store) SetSecretAt ¶
func (s *Store) SetSecretAt(account, service string, secret []byte, createdAt, updatedAt time.Time) error
SetSecretAt stores a secret with explicit create/update timestamps. A zero timestamp falls back to the current time, matching SetSecret's behavior. Implements keychain.TimestampedStore.
func (*Store) SetSecretString ¶
func (*Store) StoreKeyMetadata ¶
func (s *Store) StoreKeyMetadata(meta *KeyMetadata) error
StoreKeyMetadata records key derivation parameters for the given key version.