Documentation
¶
Overview ¶
Package security provides the cryptographic and filesystem-confinement primitives used by vrok: unguessable identifiers, password hashing and path traversal protection.
Index ¶
- Variables
- func CleanRelative(rel string) (string, error)
- func ConstantTimeEqual(a, b string) bool
- func Contained(path, root string) (string, error)
- func NewSecret() ([]byte, error)
- func OpenShared(path, root string) (*os.File, error)
- type Argon2Hasher
- type Argon2Params
- type CryptoTokenSource
- type HMACSigner
- type Hasher
- type PathResolver
- type Signer
- type TokenSource
Constants ¶
This section is empty.
Variables ¶
var ErrBadSignature = errors.New("security: invalid signature")
ErrBadSignature is returned when a signed value fails verification.
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.
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 ¶
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 ¶
ConstantTimeEqual reports whether two secrets match without leaking their contents through timing. Use it for every token and signature comparison.
func Contained ¶
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 ¶
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 ¶
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) 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 ¶
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.