Help us shape the future of Go! Take the 2026 Go Developer Survey

apppass

package
v0.2.50 Latest Latest
Warning

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

Go to latest
Published: Oct 22, 2026 License: MPL-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package apppass is the app-password mechanism the stateful personalities share: argon2id hashing in PHC form, constant-time verification, a dummy verify that hides account existence, two generators (a grouped token and a BIP-39 word phrase), and a verified-login cache. The IMAP head (chassis/server/personality/imap) and the calendar head (chassis/server/personality/calendar) authenticate the same way, so one password minted for a persona can open both — the product decides whether the credential is shared by passing the same value to both account ops.

Index

Constants

View Source
const Alphabet = passwordAlphabet

Alphabet is the character set generated passwords draw from (see passwordAlphabet: nothing that reads ambiguously when typed).

View Source
const WordPasswordDefaultWords = 5

WordPasswordDefaultWords is the default word count for GenerateWordPassword: 5 × 11 bits = 55 bits, enough that an offline attack on the argon2id hash is measured in millennia while the phrase stays typable on a phone. Four words (44 bits) is defensible only because the head throttles LOGIN; six (66 bits) costs one more word.

Variables

View Source
var (
	// ErrBadHash is returned when a stored hash is not a PHC argon2id string
	// this package produced.
	ErrBadHash = errors.New("apppass: malformed password hash")
)

Functions

func BasicHeaderMatches added in v0.2.31

func BasicHeaderMatches(header, user string, password []byte) bool

BasicHeaderMatches checks an `Authorization: Basic <token>` header value against one STATIC credential (a literal user and a password held as a secret) — the verdict-only `basic-auth-verify` op and the ipp head's v1 credential both use it. Not for account tables: those hash with argon2id (VerifyPassword).

RFC 7617: the scheme is case-insensitive, the token is base64(user-id ":" password), and a user-id may not contain a colon. Both halves are compared in constant time and BOTH comparisons always run, so timing does not reveal which half failed; length differences are the one thing ConstantTimeCompare reveals, the same trade-off hmac.Equal makes. The decoded credential is zeroed before returning.

func GenerateFrom added in v0.2.31

func GenerateFrom(alphabet string, n int) string

GenerateFrom returns n characters drawn uniformly from alphabet (1-256 distinct bytes) using crypto/rand. chassis/authn uses it for the parts of an issued password: the credential's short id and a machine secret.

func GeneratePassword

func GeneratePassword() string

GeneratePassword returns a fresh random password in four-character groups ("xxxx-xxxx-xxxx-xxxx-xxxx-xxxx", ~116 bits) for the `password=""` path of the account ops. It is returned to the caller exactly once and only its hash is stored.

func GenerateWordPassword

func GenerateWordPassword(n int) string

GenerateWordPassword returns n words from the BIP-39 list joined by hyphens ("river-galaxy-bamboo-orbit-velvet"). n < 1 uses the default. Each word is one unbiased 11-bit draw from crypto/rand: 65536 is a multiple of 2048, so the modulo introduces no bias. Returned to the caller exactly once by the account ops; only its hash is stored.

func HashPassword

func HashPassword(password string) (string, error)

HashPassword returns a PHC-formatted argon2id string ($argon2id$v=19$m=…,t=…,p=…$<salt>$<hash>, base64 without padding) for storage in an accounts table (imap_accounts.pw_hash, calendar_accounts.pw_hash).

func LoginKey

func LoginKey(username, pwHash, password string) string

LoginKey is the cache key for one verified triple.

func VerifyDummy

func VerifyDummy(password string) bool

VerifyDummy burns one argon2id verify against a fixed hash so a LOGIN for an unknown username costs the same as one for a known username (no account-existence timing oracle). The result is always false.

func VerifyPassword

func VerifyPassword(phc, password string) (bool, error)

VerifyPassword reports whether password matches the PHC hash. The comparison is constant-time; parameters are read from the hash so a later cost bump verifies old rows unchanged. A malformed hash is an error (never a silent false-negative that hides a corrupt row).

Types

type LoginCache

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

LoginCache remembers recently verified (username, hash, password) triples so a desktop client's 5–10 parallel LOGINs per wake — or a CalDAV client's dozens of Basic-authenticated requests per refresh — cost one argon2id verify, not one each. The key is a hash of all three, so a rotated password (new pw_hash) misses by construction, and a stale entry can only ever admit a password that was correct for that hash.

func NewLoginCache

func NewLoginCache(ttl time.Duration, max int) *LoginCache

NewLoginCache builds a cache holding at most max entries for ttl each.

func (*LoginCache) Hit

func (c *LoginCache) Hit(key string) bool

Hit reports whether key was verified within the TTL.

func (*LoginCache) Put

func (c *LoginCache) Put(key string)

Put records a verified key.

func (*LoginCache) SetClock

func (c *LoginCache) SetClock(now func() time.Time)

SetClock pins the cache's clock (tests).

Jump to

Keyboard shortcuts

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