kovidgoyal/kitty · error
invalid TOTP secret: %w
Error message
invalid TOTP secret: %w
What it means
generateTOTP decodes the configured secret as base32 (uppercase, no padding) before computing the HMAC-based one-time password. If decoding fails, the secret is not valid base32 and this error is returned. Called from RunSSHAskpass when a TOTP field is requested.
Source
Thrown at kittens/ssh/askpass.go:65
}
func isOTPPrompt(msg string) bool {
q := strings.ToLower(msg)
if strings.Contains(q, "passphrase") {
return false
}
if strings.Contains(q, "verification code") || strings.Contains(q, "one-time password") || strings.Contains(q, "one time password") || strings.Contains(q, "authenticator code") || strings.Contains(q, "authentication code") || strings.Contains(q, "two-factor") || strings.Contains(q, "2fa") || strings.Contains(q, "otp") || strings.Contains(q, "passcode") {
return true
}
return false
}
func generateTOTP(secret string, digits, period int64, t time.Time) (string, error) {
s := strings.ToUpper(strings.TrimSpace(secret))
s = strings.ReplaceAll(s, " ", "")
key, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(s)
if err != nil {
return "", fmt.Errorf("invalid TOTP secret: %w", err)
}
counter := uint64(t.Unix() / period)
var buf [8]byte
binary.BigEndian.PutUint64(buf[:], counter)
mac := hmac.New(sha1.New, key)
_, _ = mac.Write(buf[:])
sum := mac.Sum(nil)
off := sum[len(sum)-1] & 0x0f
code := (uint32(sum[off])&0x7f)<<24 | (uint32(sum[off+1])&0xff)<<16 | (uint32(sum[off+2])&0xff)<<8 | (uint32(sum[off+3]) & 0xff)
mod := uint32(1)
for range digits {
mod *= 10
}
val := code % mod
fmtstr := fmt.Sprintf("%%0%dd", digits)
return fmt.Sprintf(fmtstr, val), nil
}
View on GitHub (pinned to 6d5d0c4406)
Solutions
- Provide the secret as standard base32 (A-Z, 2-7), no padding
- Strip whitespace and padding; the code already uppercases and removes spaces
- Re-copy the seed from the authenticator/QR provisioning URI
- Test decode with base32.StdEncoding.WithPadding(base32.NoPadding) locally
Example fix
// before password: "hello world!" // after password: "JBSWY3DPEHPK3PXP"
Defensive patterns
Strategy: validation
Validate before calling
func validBase32(s string) bool {
s = strings.ToUpper(strings.TrimSpace(strings.ReplaceAll(s, " ", "")))
_, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(s)
return err == nil
} Type guard
func isTOTPSecret(s string) bool { return validBase32(s) } Prevention
- Copy base32 seeds exactly from the provisioning URI
- Avoid hex-format seeds
- Strip whitespace before storing
When it happens
Trigger: Configuring an ssh secret with a password field whose value contains characters outside the base32 alphabet (lowercase after normalization, digits like 0/1/8 in some alphabets, punctuation) or wrong padding.
Common situations: Pasting a TOTP seed that includes spaces handled incorrectly, hex-format seeds, or secrets copied with trailing characters/newlines.
Related errors
- Failed to create SHM file with error: %w
- Failed to write to SHM file with error: %w
- Failed to flush SHM file with error: %w
- Failed to read from SHM file with error: %w
- Failed to parse response data: %#v with error: %w
AI-assisted analysis of kovidgoyal/kitty@6d5d0c4406 (2026-08-27).
Data as JSON: /api/errors/c4edaab98c1a6635.
Report an issue: GitHub.