Skip to content

robby031/genotp-go

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

39 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

genotp-go

CI Go Report Card codecov Go Reference License: MIT

Security-focused OTP library in Go. Full implementation of HOTP (RFC 4226) and TOTP (RFC 6238) plus advanced features: context binding, per-context replay isolation, and clock skew detection.

Highlights

  • ✅ Passes all RFC 4226 & RFC 6238 test vectors (SHA1/256/512)
  • ✅ Replay protection + rate limiting with bounded memory
  • ✅ Constant-time comparison to prevent timing attacks
  • Context binding - OTP codes bound to (IP, device, session, origin)
  • Coarse location context - region / geo bucket / distance class helpers
  • Per-context replay isolation - code collisions between users don't block each other
  • Anti-phishing origin binding - origin URL automatically normalized
  • SecretProvider support - additive path for wrapped or externally-resolved secrets
  • HMACProvider support - non-exportable key path for HSM-native OTP
  • Explicit batch verification - additive per-item verify APIs for bulk workloads
  • Clock skew detector with opt-in auto-adjust
  • ✅ Compatible with Google Authenticator / Authy / Microsoft Authenticator (default mode)
  • ✅ Comprehensive test coverage

Installation

go get github.com/robby031/genotp-go

Basic Usage

Standard TOTP (Google Authenticator compatible)

package main

import (
    "fmt"
    "github.com/robby031/genotp-go"
)

func main() {
    secret, _ := genotp.CreateSecret()
    code, _ := genotp.GenTotpDefault(secret)
    valid, _ := genotp.VerifyTotpDefault(secret, code)
    
    fmt.Printf("Code: %s, Valid: %v\n", code, valid)
}

Builder pattern (more ergonomic)

secret, _ := genotp.CreateSecret()

totp, _ := genotp.NewTotpBuilder().
    Secret(secret).
    Algorithm(genotp.SHA1).
    Digits(6).
    Period(30).
    Build()

code, _ := totp.Generate(nil)
valid, _ := totp.Verify(code, nil, 1)

QR code for authenticator app

uri := genotp.NewOtpAuthUri(genotp.TotpType, "ACME:alice@example.com", genotp.EncodeBase32(secret)).
    Issuer("ACME").
    Algorithm(genotp.SHA1).
    Digits(6).
    Period(30).
    Build()

// Render `uri` to QR code (e.g., with a QR code library)

Google Authenticator migration QR (otpauth-migration://)

accounts := []genotp.OtpAuthMigrationAccount{
    {
        Type:      genotp.TotpType,
        Label:     "alice@example.com",
        Issuer:    "Example",
        SecretB32: "JBSWY3DPEHPK3PXP",
        Algorithm: genotp.SHA1,
        Digits:    6,
        Period:    30,
    },
}

uri, err := genotp.BuildOtpAuthMigrationURI(accounts, &genotp.OtpAuthMigrationOptions{
    Version:    1,
    BatchSize:  1,
    BatchIndex: 0,
    BatchID:    123456,
})
if err != nil {
    log.Fatal(err)
}

payload, err := genotp.ParseOtpAuthMigrationURI(uri)
if err != nil {
    log.Fatal(err)
}

fmt.Println(payload.Accounts[0].Label) // alice@example.com

For comprehensive usage examples covering all features — including HOTP/TOTP, builder/config patterns, context binding, verifier, clock skew detection, metrics, and production recommendations — see docs/usage.md.

For external key-manager integrations, see docs/provider_adapters.md for adapter patterns covering wrapped-secret and non-exportable HSM/KMS flows.

SecretProvider-backed OTP

provider := genotp.SecretProviderFunc(func() ([]byte, error) {
    // Example: unwrap from KMS or fetch from a secure external store.
    return wrappedSecretResolver()
})

totp, err := genotp.NewTOTPFromSecretProvider(provider, genotp.SHA1, 6, 30)
if err != nil {
    log.Fatal(err)
}

This mode is additive and keeps the existing API intact. In this v1 design, the provider resolves secret material on demand for each OTP operation, then the temporary buffer is zeroed after use. It improves encrypted-at-rest and external-secret-manager workflows, while leaving room for a future non-exportable HSM-backed HMAC mode.

HMACProvider-backed OTP

provider := genotp.HMACProviderFunc(func(algorithm genotp.Algorithm, message []byte) ([]byte, error) {
    // Example: delegate to an HSM-native or remote HMAC service.
    return hsmSign(algorithm, message)
})

hotp, err := genotp.NewHOTPFromHMACProvider(provider, genotp.SHA256, 6)
if err != nil {
    log.Fatal(err)
}

This path is intended for non-exportable OTP keys. The library sends only the counter/time-step message to the provider and receives the HMAC output back.

Context binding — anti channel OTP intercept (flagship feature)

hotp, _ := genotp.NewHOTP(secret, genotp.SHA1, 6)

// Server binds code to (session + IP hash) of user at issue time:
ctx := genotp.NewOtpContextBuilder().
    Session("login-abc123").
    IP("hash_of_user_ip").
    Build()

code, _ := hotp.GenBound(counter, ctx)
// Send `code` via any channel (SMS, email, WhatsApp, Telegram, push notif, ...).

// When user submits:
if hotp.VerifyBound(form.Code, counter, ctx) {
    // ✓ code correct AND context matches
}
// Attacker who intercepts code from different IP/session -> automatically rejected.

Benchmarks 07-06-2026

go test ./tests -run=^$ -bench=. -benchmem
goos: darwin
goarch: arm64
pkg: github.com/robby031/genotp-go/tests
cpu: Apple M4
BenchmarkHOTPGenerate-10                        11958049               100.7 ns/op             8 B/op          1 allocs/op
BenchmarkHOTPVerify-10                          12271438                98.93 ns/op            0 B/op          0 allocs/op
BenchmarkTOTPGenerate-10                         9337573               132.1 ns/op             8 B/op          1 allocs/op
BenchmarkTOTPGenerateAtFixedTime-10             11920894               100.5 ns/op             8 B/op          1 allocs/op
BenchmarkTOTPVerify-10                           4001732               303.8 ns/op             0 B/op          0 allocs/op
BenchmarkTOTPVerifyWindow0-10                   12422841                98.22 ns/op            0 B/op          0 allocs/op
BenchmarkGenerateSecretDefault-10                6406161               185.3 ns/op            24 B/op          1 allocs/op
BenchmarkGenerateSecret256-10                    6735511               179.3 ns/op            32 B/op          1 allocs/op
BenchmarkBase32Encode-10                        61364334                18.57 ns/op           32 B/op          1 allocs/op
BenchmarkBase32Decode-10                        24849778                49.38 ns/op            0 B/op          0 allocs/op
BenchmarkProvisioningURITOTP-10                  3556134               331.0 ns/op           512 B/op         12 allocs/op
BenchmarkProvisioningURIHOTP-10                  3594416               331.2 ns/op           512 B/op         12 allocs/op
BenchmarkReplayProtectionVerify-10               5728464               213.9 ns/op            76 B/op          1 allocs/op
BenchmarkRateLimiterContention-10                 186141              6520 ns/op             112 B/op          5 allocs/op
BenchmarkConcurrentHOTPVerify-10                   43528             27301 ns/op             208 B/op          5 allocs/op
BenchmarkTOTPVerifyParallel-10                  71319079                16.08 ns/op            0 B/op          0 allocs/op
BenchmarkVerifierParallelFreshCodes-10           2907163               377.3 ns/op            76 B/op          1 allocs/op
PASS
ok      github.com/robby031/genotp-go/tests     24.891s

Features

HOTP (RFC 4226)

  • Generate and verify HMAC-based One-Time Passwords
  • Look-ahead resynchronization for counter drift
  • Context binding for enhanced security

TOTP (RFC 6238)

  • Time-based One-Time Passwords with configurable period
  • Window-based verification for clock skew tolerance
  • Support for SHA1, SHA256, and SHA512 algorithms
  • Context binding and clock skew tracking

Context Binding

Bind OTP codes to specific contexts:

  • IP address (or hash thereof)
  • Device identifier
  • Session ID
  • Origin URL (anti-phishing)
  • Region or geo bucket (coarse location context)
  • Distance class (same_area, nearby, far)
  • Custom fields

For production use, prefer coarse, application-defined location labels over raw latitude/longitude. This keeps OTP flows more stable across GPS jitter, platform differences, and privacy constraints.

Two modes:

  1. HMAC binding: Different contexts produce different OTP codes
  2. Verifier-stored: Standard OTP codes, but server validates context

Clock Skew Detection

Track and compensate for clock drift between client and server:

  • Passive mode: only reports statistics
  • Active mode: automatically adjusts verification window
  • Recommends for window sizing or NTP sync

Replay Protection

Prevent OTP code reuse with:

  • Per-context replay isolation
  • Configurable rate limiting
  • Bounded memory usage

API Reference

Core Types

  • Algorithm: SHA1, SHA256, SHA512
  • HOTP: HMAC-based OTP implementation
  • TOTP: Time-based OTP implementation
  • OtpContext: Context binding data
  • ClockSkewDetector: Clock drift tracking
  • Verifier: Replay protection and rate limiting (per-instance)
  • ReplayStore: pluggable backend untuk replay-set (default = in-memory bounded + TTL; untuk multi-replica deployment implement dengan Redis / etcd / sql — lihat docs/distributed_replay_protection.md)

Helper Functions

  • CreateSecret(): Generate a random 160-bit secret
  • GenHotpDefault(): Generate HOTP with default parameters
  • GenTotpDefault(): Generate TOTP with default parameters
  • VerifyHotpDefault(): Verify HOTP with default parameters
  • VerifyTotpDefault(): Verify TOTP with default parameters
  • EncodeBase32(data []byte) string: Encode bytes to Base32 (RFC 4648, no padding)
  • DecodeBase32(dst []byte, src string) (int, error): Decode Base32 ke buffer caller. Strip ASCII whitespace, -, dan = otomatis. Mengembalikan jumlah byte yang ditulis. Returns ErrDstTooSmall jika dst kekecilan, ErrInvalidSecret jika ada karakter invalid.

Testing

# Run all tests
go test ./tests

# Run tests with coverage
go test -v -race -coverprofile=coverage.out ./tests/

# Run fuzz tests (locally)
go test -fuzz=FuzzHOTPGenerate -fuzztime=1m ./fuzz/
go test -fuzz=FuzzTOTPVerify -fuzztime=1m ./fuzz/

All RFC test vectors are included and verified:

  • RFC 4226 HOTP test vectors
  • RFC 6238 TOTP test vectors (SHA1, SHA256, SHA512)

Continuous Integration

The project uses GitHub Actions for automated testing on every push to main and pull requests:

CI Workflow includes:

  • Tests - Run on Go 1.21, 1.22, and 1.23 with race detection
  • Fuzz Tests - 30 seconds per fuzz target (10 fuzz functions total)
  • Linting - golangci-lint with 15+ enabled linters
  • Security Scan - gosec static analysis
  • Build Verification - Ensures code compiles and go.mod is tidy
  • Code Coverage - Uploaded to Codecov

See .github/workflows/ci.yml for the full configuration.

License

MIT — see LICENSE

About

A security-focused, high-performance OTP (TOTP/HOTP) library in Go featuring context binding, anti-phishing, clock skew detection, and replay isolation.

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Sponsor this project

Packages

 
 
 

Contributors