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 without this a fresh // instance on a public address would belong to whoever found it first. The // call must carry this token, which is generated at startup and written only // to the server log: proof that the caller can read the server'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 }