security

package
v0.6.1 Latest Latest
Warning

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

Go to latest
Published: Oct 5, 2026 License: MIT Imports: 12 Imported by: 0

Documentation

Overview

Package security provides the cryptographic and filesystem-confinement primitives used by vrok: unguessable identifiers, password hashing and path traversal protection.

Index

Constants

This section is empty.

Variables

View Source
var ErrBadSignature = errors.New("security: invalid signature")

ErrBadSignature is returned when a signed value fails verification.

View Source
var ErrPasswordMismatch = errors.New("security: password mismatch")

ErrPasswordMismatch is returned when a supplied password does not match a stored hash. It carries no detail on purpose.

View Source
var ErrPathEscape = errors.New("security: path escapes shared root")

ErrPathEscape is returned when a request resolves outside the shared root. Callers must translate it into a 404 rather than a 403: telling a visitor that a path exists but is forbidden is already a disclosure.

Functions

func CleanRelative

func CleanRelative(rel string) (string, error)

CleanRelative normalises an untrusted relative path and rejects anything that tries to climb above its own root. It operates purely on strings, so it is safe to call before any filesystem access.

func ConstantTimeEqual

func ConstantTimeEqual(a, b string) bool

ConstantTimeEqual reports whether two secrets match without leaking their contents through timing. Use it for every token and signature comparison.

func Contained

func Contained(path, root string) (string, error)

Contained re-resolves path and confirms it is still the approved location.

For a single-file share (root empty) the last path component must still be a regular file: replacing it with a symlink after the share was created would otherwise redirect visitors to a secret. Intermediate directories may be OS-level links (macOS /var → /private/var); those are not a swap.

For a directory share, the resolved path must still live under root.

func NewSecret

func NewSecret() ([]byte, error)

NewSecret returns a 256-bit key for signing process-local artefacts such as authentication cookies. Secrets are never persisted: restarting vrok invalidates every cookie it ever issued, which is the behaviour we want for a tool whose shares die with the process.

func OpenShared

func OpenShared(path, root string) (*os.File, error)

OpenShared opens path only after confirming it has not been redirected outside what the user approved.

A share records a canonical path at creation time. Between then and a visitor's request the user (or something on the machine) could replace that path with a symlink to a secret. Opening blindly would serve the secret. This function re-resolves and refuses anything that is no longer the approved file, or — when root is set — anything that no longer lives under the shared directory.

Types

type Argon2Hasher

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

Argon2Hasher implements Hasher using Argon2id. Plaintext passwords are never stored or logged; only the encoded digest below ever leaves this type.

func NewArgon2Hasher

func NewArgon2Hasher(p Argon2Params) *Argon2Hasher

NewArgon2Hasher returns a Hasher with the given cost parameters. Zero-valued fields fall back to DefaultArgon2Params.

func (*Argon2Hasher) Hash

func (h *Argon2Hasher) Hash(password string) (string, error)

Hash returns a PHC-formatted Argon2id digest: $argon2id$v=19$m=65536,t=1,p=4$<salt>$<key>

func (*Argon2Hasher) Verify

func (h *Argon2Hasher) Verify(password, encoded string) error

Verify recomputes the digest with the parameters recorded in encoded and compares it in constant time.

type Argon2Params

type Argon2Params struct {
	Time    uint32 // iterations
	Memory  uint32 // KiB
	Threads uint8
	KeyLen  uint32
	SaltLen uint32
}

Argon2Params configures the Argon2id cost. The defaults follow the OWASP recommendation for interactive logins.

func DefaultArgon2Params

func DefaultArgon2Params() Argon2Params

DefaultArgon2Params returns the cost used by the CLI: 64 MiB and one pass, which takes tens of milliseconds and makes offline guessing expensive.

type CryptoTokenSource

type CryptoTokenSource struct{}

CryptoTokenSource is the production TokenSource, backed by crypto/rand.

func NewCryptoTokenSource

func NewCryptoTokenSource() CryptoTokenSource

NewCryptoTokenSource returns a TokenSource suitable for production use.

func (CryptoTokenSource) NewID

func (CryptoTokenSource) NewID() (string, error)

NewID implements TokenSource.

func (CryptoTokenSource) NewToken

func (CryptoTokenSource) NewToken() (string, error)

NewToken implements TokenSource.

type HMACSigner

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

HMACSigner signs with HMAC-SHA256 under a process-local key.

func NewHMACSigner

func NewHMACSigner(key []byte) *HMACSigner

NewHMACSigner returns a Signer using key. Generate the key with NewSecret so that it is fresh on every run and signatures do not outlive the process.

func (*HMACSigner) Sign

func (s *HMACSigner) Sign(message string) string

Sign implements Signer.

func (*HMACSigner) Verify

func (s *HMACSigner) Verify(message, signature string) error

Verify implements Signer.

type Hasher

type Hasher interface {
	Hash(password string) (string, error)
	Verify(password, encoded string) error
}

Hasher turns a plaintext password into a verifiable digest. The server depends on this interface so the hashing cost can be lowered in tests.

type PathResolver

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

PathResolver maps an untrusted, request-supplied relative path onto a real file inside a single directory. It is the only component allowed to turn visitor input into a filesystem path.

Confinement is enforced twice: lexically, by rejecting any path that climbs out of the root after cleaning, and physically, by resolving symlinks on the result and requiring it to still live under the root. The second check is what stops a symlink inside the share from pointing at /etc/passwd.

func NewPathResolver

func NewPathResolver(root string) (*PathResolver, error)

NewPathResolver resolves root to a canonical absolute path and returns a resolver confined to it.

func (*PathResolver) Resolve

func (r *PathResolver) Resolve(rel string) (string, error)

Resolve maps a URL-style relative path to an absolute path inside the root. The returned path is guaranteed to exist and to be contained by the root.

func (*PathResolver) Root

func (r *PathResolver) Root() string

Root returns the canonical shared directory.

type Signer

type Signer interface {
	Sign(message string) string
	Verify(message, signature string) error
}

Signer mints and checks short opaque proofs. vrok uses it for the cookie that records "this visitor already entered the password", so that the password itself never has to be stored client-side or re-sent per request.

type TokenSource

type TokenSource interface {
	// NewID returns a short, human-quotable share handle.
	NewID() (string, error)
	// NewToken returns the high-entropy secret embedded in a share URL.
	NewToken() (string, error)
}

TokenSource hands out the random values a share needs. The server and sharing layers depend on this interface rather than on crypto/rand directly so tests can supply deterministic values.

Jump to

Keyboard shortcuts

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