package fwh_pro import ( "crypto/hmac" "crypto/sha1" "encoding/base32" "encoding/binary" "fmt" "strings" "time" ) // generateTOTP implements RFC 6238 with the common SHA-1, 6-digit, 30-second profile. func generateTOTP(secret string, now time.Time) (string, error) { secret = strings.ToUpper(strings.Join(strings.Fields(secret), "")) secret = strings.TrimRight(secret, "=") key, err := base32.StdEncoding.WithPadding(base32.NoPadding).DecodeString(secret) if err != nil || len(key) == 0 { return "", fmt.Errorf("invalid FWH TOTP secret: expected a Base32 key") } var counter [8]byte binary.BigEndian.PutUint64(counter[:], uint64(now.Unix()/30)) mac := hmac.New(sha1.New, key) _, _ = mac.Write(counter[:]) digest := mac.Sum(nil) offset := digest[len(digest)-1] & 0x0f value := binary.BigEndian.Uint32(digest[offset:offset+4]) & 0x7fffffff return fmt.Sprintf("%06d", value%1000000), nil } // GenerateTOTP uses the same profile as the FWH login request. func GenerateTOTP(secret string, now time.Time) (string, error) { return generateTOTP(secret, now) }