Documentation
¶
Overview ¶
Package totp provides a high-level API for generating, encrypting, validating, and managing Time-based One-Time Passwords (TOTP) and related recovery codes.
This package bundles together everything an application needs to implement multi-factor authentication based on RFC 6238 including secret key creation, URI generation compatible with authenticator applications, one-time-password generation/validation, AES-256 encryption helpers for safely persisting secrets, and secure recovery-code utilities.
By keeping functionality self-contained the package eliminates direct dependencies on third-party TOTP libraries and allows services to remain framework-agnostic while still following contemporary security best-practices.
Architecture ¶
Internally the package is divided into three cohesive layers.
crypto – helpers in aes256.go implement symmetric encryption/decryption of the secret key with AES-256-GCM as well as random key generation utilities.
totp – functions in otp.go provide secret key generation (GenerateSecretKey), HOTP/TOTP code calculation (GenerateTOTP/ValidateTOTP/GenerateHOTP) and convenient URI construction (GetTOTPURI) for onboarding to Google Authenticator, 1Password and compatible apps.
recovery – helpers in recovery.go create, hash and verify single-use recovery codes that can be offered to users in case they permanently lose access to their authenticator device.
Configuration such as the encryption key is loaded once per process via the env tag aware loader in config.go. The required environment variable name is TOTP_ENCRYPTION_KEY and it must contain a Base64 encoded 32-byte key suitable for AES-256.
Usage ¶
The minimal happy path for enrolling a user looks like this:
package main import ( "fmt" "github.com/dmitrymomot/saaskit/pkg/totp" ) func main() { // 1. Create a brand-new secret secret, _ := totp.GenerateSecretKey() // 2. Persist the secret encrypted in your datastore var cfg totp.Config // Load config from environment using github.com/caarlos0/env // env.Parse(&cfg) key, _ := totp.GetEncryptionKey(cfg) encSecret, _ := totp.EncryptSecret(secret, key) // 3. Display the bootstrap URI/QR code to the user uri, _ := totp.GetTOTPURI(totp.TOTPParams{ Secret: secret, AccountName: "alice@example.com", Issuer: "Acme", }) fmt.Println(uri) // 4. Later – validate an OTP provided by the user ok, _ := totp.ValidateTOTP(secret, "123456") fmt.Println(ok) }
Additional helpers exist for generating time-window agnostic codes (GenerateTOTPWithTime), producing export-friendly Base64 keys (GenerateEncodedEncryptionKey) and creating or verifying recovery codes (GenerateRecoveryCodes, HashRecoveryCode, VerifyRecoveryCode).
Error Handling ¶
Every exported operation returns a descriptive error that may be wrapped using errors.Join. Inspect errors with errors.Is against package level sentinels such as ErrInvalidSecret, ErrFailedToEncryptSecret, ErrInvalidOTP etc.
See Also ¶
- RFC 4226 – HMAC-Based One-Time Password (HOTP) Algorithm
- RFC 6238 – Time-Based One-Time Password (TOTP) Algorithm
To explore more usage scenarios refer to the package level examples and unit-tests.
Index ¶
- Constants
- Variables
- func DecryptSecret(cipherTextBase64 string, key []byte) (string, error)
- func EncryptSecret(plainText string, key []byte) (string, error)
- func GenerateEncodedEncryptionKey() (string, error)
- func GenerateEncryptionKey() ([]byte, error)
- func GenerateHOTP(key []byte, counter int64, digits int) int
- func GenerateRecoveryCodes(count int) ([]string, error)
- func GenerateSecretKey() (string, error)
- func GenerateTOTP(secret string) (string, error)
- func GenerateTOTPWithTime(secret string, t time.Time) (string, error)
- func GetEncryptionKey(cfg Config) ([]byte, error)
- func GetTOTPURI(params TOTPParams) (string, error)
- func HashRecoveryCode(code string) string
- func ValidateTOTP(secret, otp string) (bool, error)
- func VerifyRecoveryCode(code, hashedCode string) bool
- type Config
- type TOTPParams
Constants ¶
const ( DefaultDigits = 6 // Standard 6-digit TOTP codes DefaultPeriod = 30 // 30-second validity window (RFC 6238 standard) DefaultAlgorithm = "SHA1" // HMAC-SHA1 algorithm (RFC 6238 standard) )
const (
AESKeySize = 32 // Required key size for AES-256 (256 bits / 8 = 32 bytes)
)
Variables ¶
var ( ErrFailedToEncryptSecret = errors.New("failed to encrypt TOTP secret") ErrFailedToDecryptSecret = errors.New("failed to decrypt TOTP secret") ErrInvalidCipherTooShort = errors.New("cipher text too short") ErrFailedToGenerateEncryptionKey = errors.New("failed to generate encryption key") ErrFailedToLoadEncryptionKey = errors.New("failed to load encryption key") ErrInvalidEncryptionKeyLength = errors.New("invalid encryption key length") ErrFailedToGenerateSecretKey = errors.New("failed to generate TOTP secret key") ErrFailedToValidateTOTP = errors.New("failed to validate TOTP") ErrMissingSecret = errors.New("missing secret") ErrInvalidSecret = errors.New("invalid secret") ErrMissingAccountName = errors.New("missing account name") ErrMissingIssuer = errors.New("missing issuer") ErrEncryptionKeyNotSet = errors.New("TOTP encryption key not set") ErrInvalidOTP = errors.New("invalid OTP format") ErrInvalidRecoveryCodeCount = errors.New("invalid recovery code count, must be greater than 0") ErrFailedToGenerateRecoveryCode = errors.New("failed to generate recovery code") ErrFailedToGenerateTOTP = errors.New("failed to generate TOTP") )
var ( // ValidateSecretKeyRegex ensures Base32 format: uppercase A-Z, digits 2-7, optional padding ValidateSecretKeyRegex = regexp.MustCompile("^[A-Z2-7]+=*$") )
Functions ¶
func DecryptSecret ¶
DecryptSecret decrypts the encrypted TOTP secret. Expects the ciphertext as a base64-encoded string.
func EncryptSecret ¶
EncryptSecret encrypts the TOTP secret using AES-256-GCM. Returns the ciphertext as a base64-encoded string.
func GenerateEncodedEncryptionKey ¶
GenerateEncodedEncryptionKey generates a new random 32-byte key suitable for AES-256 encryption. Returns the generated key as a base64-encoded string or an error if the random number generation fails. This function is useful for generating a new key and storing it in the configuration.
func GenerateEncryptionKey ¶
GenerateEncryptionKey creates a new random 32-byte key suitable for AES-256 encryption. Returns the generated key or an error if the random number generation fails.
func GenerateHOTP ¶
GenerateHOTP implements RFC 4226 HMAC-based One-Time Password algorithm. The algorithm converts a counter value into a numeric code using HMAC-SHA1.
func GenerateRecoveryCodes ¶
GenerateRecoveryCodes creates cryptographically secure backup codes for account recovery. Each code is a 16-character hexadecimal string (64 bits of entropy).
func GenerateSecretKey ¶
GenerateSecretKey generates a new Base32-encoded secret key for TOTP.
func GenerateTOTP ¶
GenerateTOTP generates a time-based one-time password for the current 30-second window. The secret must be a valid Base32-encoded string.
func GenerateTOTPWithTime ¶
GenerateTOTPWithTime generates a TOTP code for the 30-second window containing the specified time. Useful for testing or generating codes for specific moments.
func GetEncryptionKey ¶
GetEncryptionKey decodes the encryption key from the configuration. The key must be a 32-byte base64-encoded string. Returns the decoded key bytes or an error if decoding fails or the key length is invalid.
func GetTOTPURI ¶
func GetTOTPURI(params TOTPParams) (string, error)
GetTOTPURI creates a properly encoded TOTP URI for use with authenticator apps. The URI format follows the Key Uri Format specification: https://github.com/google/google-authenticator/wiki/Key-Uri-Format
func HashRecoveryCode ¶
HashRecoveryCode creates a SHA-256 hash for secure storage of recovery codes.
func ValidateTOTP ¶
ValidateTOTP validates the TOTP code provided by the user.
func VerifyRecoveryCode ¶
VerifyRecoveryCode performs constant-time comparison to prevent timing attacks. Essential for security: comparison time must not reveal information about where differences occur.
Types ¶
type TOTPParams ¶
type TOTPParams struct { Secret string // Base32-encoded TOTP secret key (required) AccountName string // User identifier like email (required) Issuer string // Service name displayed in authenticator apps (required) Algorithm string // HMAC algorithm (optional, defaults to SHA1) Digits int // Number of digits in generated codes (optional, defaults to 6) Period int // Code validity period in seconds (optional, defaults to 30) }
TOTPParams contains the parameters for TOTP URI generation
func (TOTPParams) GetDefaults ¶
func (p TOTPParams) GetDefaults() TOTPParams
GetDefaults returns a copy with RFC 6238 standard defaults applied to zero-valued fields
func (TOTPParams) Validate ¶
func (p TOTPParams) Validate() error
Validate ensures all required TOTP parameters are present and valid