Help us shape the future of Go! Take the 2026 Go Developer Survey

totp

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 18, 2026 License: MIT Imports: 13 Imported by: 0

README

totp-go

Go Reference

Go implementation of RFC 6238 (TOTP) and RFC 4226 (HOTP) with backup recovery codes.

Features

  • Strictly follows RFC 6238 and RFC 4226, verified against official RFC test vectors.
  • Standard library only (crypto/hmac, crypto/rand, crypto/subtle, encoding/base32).
  • Constant-time code comparison with crypto/subtle.
  • ValidateWithStep returns the counter step to prevent replay attacks within the 30-second window.
  • Generates xxxx-xxxx backup codes using 32 unambiguous characters (23456789abcdefghjkmnpqrstuvwxyz, skipping 0, 1, i, l, o).
  • Zero modulo bias because 256 divides evenly by 32.
  • Generates secrets in ~145 ns and recovery codes in ~560 ns.

Installation

go get github.com/fumbledlol/totp-go

Usage

1. Enrollment (secret and QR code URL)
package main

import (
	"fmt"
	"log"

	"github.com/fumbledlol/totp-go"
)

func main() {
	secret, err := totp.GenerateSecret()
	if err != nil {
		log.Fatal(err)
	}

	authURL := totp.BuildURL("Acme", "alice@example.com", secret)
	fmt.Println("Secret:", secret)
	fmt.Println("URL:", authURL)
}
2. Verify passcodes
valid := totp.Validate("123456", secret)
if valid {
    fmt.Println("Code accepted")
}
3. Replay prevention

To stop users or attackers from reusing the same passcode during its 30-second window, record the consumed counter step in Redis or SQL:

valid, step := totp.ValidateWithStep(passcode, userSecret)
if !valid {
    return errors.New("invalid or expired passcode")
}

if step <= user.LastUsedTOTPStep {
    return errors.New("passcode already used")
}

user.LastUsedTOTPStep = step
4. Backup recovery codes

Generate, normalize, and hash backup codes:

codes, err := totp.GenerateRecoveryCodes(10)
if err != nil {
    log.Fatal(err)
}

// Show these to the user once:
for _, code := range codes {
    fmt.Println(code) // e.g. 7m4k-9y2p
}

// Store hashes with your server pepper
serverPepper := []byte("your-secret-server-pepper")
for _, code := range codes {
    hash := totp.HashRecoveryCode(code, serverPepper)
    // save hash to database...
}

// When validating user input:
inputCode := "7M4K 9Y2P" // handles spaces, dashes, and casing
inputHash := totp.HashRecoveryCode(inputCode, serverPepper)

Benchmarks

BenchmarkGenerateSecret-12             8068794       144.5 ns/op        32 B/op       1 allocs/op
BenchmarkValidate-12                    716224      1457.0 ns/op      1024 B/op      16 allocs/op
BenchmarkGenerateRecoveryCodes-12       2140929       562.6 ns/op       400 B/op      12 allocs/op
BenchmarkHashRecoveryCode-12           1605768       758.0 ns/op       664 B/op      10 allocs/op

License

MIT

Documentation

Index

Constants

View Source
const (
	// DefaultPeriod is the standard TOTP time-step duration (30 seconds).
	DefaultPeriod = 30 * time.Second

	// DefaultDigits is the standard number of digits in a TOTP passcode.
	DefaultDigits = 6

	// DefaultSkew is the number of adjacent intervals allowed before and after current time.
	DefaultSkew = 1
)

Variables

This section is empty.

Functions

func BuildURL

func BuildURL(issuer, accountName, secret string) string

BuildURL formats an otpauth:// URL for authenticator apps.

func GenerateCode

func GenerateCode(secret string, t time.Time) (string, error)

GenerateCode computes the 6-digit TOTP passcode for a given secret at the specified time.

func GenerateHOTP

func GenerateHOTP(key []byte, counter uint64) string

GenerateHOTP computes an RFC 4226 HMAC-SHA1 passcode for a given counter.

func GenerateRecoveryCodes

func GenerateRecoveryCodes(count int) ([]string, error)

GenerateRecoveryCodes generates count random recovery codes formatted as xxxx-xxxx.

func GenerateSecret

func GenerateSecret() (string, error)

GenerateSecret creates a 20-byte base32 secret.

func GenerateSecretWithLength

func GenerateSecretWithLength(numBytes int) (string, error)

GenerateSecretWithLength creates a base32 secret with the specified byte length.

func HashRecoveryCode

func HashRecoveryCode(code string, pepper []byte) string

HashRecoveryCode hashes the normalized recovery code with HMAC-SHA256 using a server pepper.

func NormalizeRecoveryCode

func NormalizeRecoveryCode(code string) string

NormalizeRecoveryCode strips hyphens and whitespace and converts the code to lowercase.

func Validate

func Validate(passcode, secret string) bool

Validate checks if a 6-digit TOTP passcode matches the secret within the default skew window.

func ValidateCustom

func ValidateCustom(passcode, secret string, t time.Time, skew int) (bool, int64)

ValidateCustom validates a passcode against a specific timestamp and skew window.

func ValidateWithStep

func ValidateWithStep(passcode, secret string) (bool, int64)

ValidateWithStep checks if a passcode matches the secret and returns the matching time-step counter.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL