package auth import ( "crypto/rand" "crypto/subtle" "encoding/hex" ) // SetupToken guards the first-admin registration. Until the first account // exists, register makes whoever calls it the admin, so a fresh instance on a // public address belonged to whoever found it first. Now that call also has // to carry this token, which is generated at startup and written only to the // server log: proof that the caller can read the operator's logs. // // The token lives in memory. A restart mints a new one and logs it again, // which is the behaviour wanted: an old token from a log line someone else // saw stops working. type SetupToken struct { value string } // NewSetupToken mints a 128-bit token. func NewSetupToken() (*SetupToken, error) { b := make([]byte, 16) if _, err := rand.Read(b); err != nil { return nil, err } return &SetupToken{value: hex.EncodeToString(b)}, nil } // Value returns the token for logging. func (t *SetupToken) Value() string { if t == nil { return "" } return t.value } // Matches reports whether supplied is the token, in constant time. A nil // token never matches anything, so a missing token fails closed. func (t *SetupToken) Matches(supplied string) bool { if t == nil || t.value == "" || supplied == "" { return false } return subtle.ConstantTimeCompare([]byte(t.value), []byte(supplied)) == 1 }