Documentation
¶
Overview ¶
Package credential generates and checks the shared secrets a player proves themselves with: the recovery code from docs/adr/0006 and the PIN from docs/adr/0007.
Like the rest of the business logic it knows neither the database nor HTTP (CLAUDE.md, "Konventionen"). It hands out strings and verifies them; who stores them is somebody else's problem.
Index ¶
Constants ¶
const ( MinPINLength = 6 MaxPINLength = 32 )
The bounds on a PIN. docs/adr/0007 sets the floor at six digits, which only holds up because of the brake in front of it; the ceiling is here so the field cannot become an essay, and nothing needs it.
They live in this package rather than beside the handler because the form that collects a PIN needs them too, and two copies of a rule drift.
Variables ¶
var ErrMalformedHash = errors.New("stored credential hash is malformed")
ErrMalformedHash reports a stored hash this package cannot read. It means the row is broken, not that the secret was wrong, and callers have to keep the two apart: answering "wrong code" to a corrupted row would send somebody looking for a code that was right all along.
Functions ¶
func Hash ¶
Hash derives the digest that gets stored for secret.
The result carries its own parameters and salt, in the format Argon2 implementations agree on:
$argon2id$v=19$m=65536,t=2,p=4$<salt>$<digest>
Self-describing on purpose. Raising the cost later has to leave the rows written before it verifiable, or the change locks out everybody who has not signed in since.
func NewCode ¶
func NewCode() string
NewCode returns a fresh recovery code, formatted for reading aloud.
Generated, never chosen. docs/adr/0006 is explicit about why: a field somebody may type their own secret into becomes a field somebody types their company password into.
func NewRecoveryCode ¶
func NewRecoveryCode() (code, hash string)
NewRecoveryCode returns a fresh code to show and the hash to store.
The two come from one call because they must not be derived apart. The hash covers the *normalized* code, which is what a sign-in compares against after running what somebody typed through NormalizeCode. Hashing the formatted string instead would reject every code the moment it was typed back without its hyphens — which is how most people would type it.
func NormalizeCode ¶
NormalizeCode turns what somebody typed into what was generated.
It folds case, drops the hyphens and anything else that is not part of the alphabet, and maps the characters the alphabet leaves out onto the ones it kept: O to zero, I and L to one. Somebody reading a code off a screen and typing "O" where a "0" stands is the expected case, not an attack, and a sign-in that refuses it for that is refusing the right person.
The result is not checked for length or plausibility. That belongs to whoever compares it against a hash, where a wrong code and a mistyped one have to look the same anyway.
Types ¶
This section is empty.