Skip to content
/ otp Public

A high-performance, zero-dependency Go package for generating and validating TOTP, HOTP and OCRA one-time passwords — RFC 4226, RFC 6238 and RFC 6287 compliant.

License

Notifications You must be signed in to change notification settings

Ja7ad/otp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

otp codecov Go Report Card Go Reference

🔐 OTP

A high-performance, zero-dependency Go package for generating and validating TOTP, HOTP and OCRA one-time passwords — RFC 4226, RFC 6238 and RFC 6287 compliant.

✨ Features

  • Zero dependencies – fully self-contained, no external packages
  • High performance with low allocations
  • Supports HOTP (RFC 4226), TOTP (RFC 6238) and OCRA (RFC 6287) algorithms
  • Configurable OTP digit lengths: 6, 8, or 10
  • Supports SHA1, SHA256, and SHA512 HMAC algorithms
  • Constant-time OTP validation to prevent timing attacks
  • Clock skew tolerance for TOTP validation
  • Generates otpauth:// URLs for Google Authenticator and compatible apps
  • Parses otpauth:// URLs into configuration structs
  • Secure random secret generation (base32 encoded)
  • Thoroughly tested against official RFC test vectors
  • Includes fuzz tests, benchmark coverage, and solid algorithm validation

Here’s your updated README.md Installation section with release and Docker image info:

📦 Installation (Go >= 1.24)

🛠️ Using Go

go get -u github.com/Ja7ad/otp

Node.js bindings are available here.


🚀 Prebuilt Binary

Download the latest CLI/API binary for your platform from the latest release page.

Online demo: https://otp-api.leapcell.app/docs

$ otp -serve localhost:8080
2025/04/06 10:41:48 INFO starting server address=:8080
2025/04/06 10:41:50 INFO request method=GET path=/docs/index.html status=200 duration=740.394µs
2025/04/06 10:41:51 INFO request method=GET path=/docs/doc.json status=200 duration=803.67µs
2025/04/06 10:41:53 INFO request method=GET path=/ status=200 duration=149.042µs
2025/04/06 10:41:54 INFO request method=GET path=/docs status=302 duration=24.444µs
Method Path Description
POST /totp/generate Generate a TOTP code
POST /totp/validate Validate a TOTP code
POST /hotp/generate Generate a HOTP code
POST /hotp/validate Validate a HOTP code
POST /ocra/generate Generate an OCRA code
POST /ocra/validate Validate an OCRA code
GET /otp/secret Generate a random base32 secret
POST /otp/url Generate otpauth URL
GET /ocra/suites List supported OCRA suites
POST /ocra/suite Parse and describe suite config

🐳 Docker Image

You can also run the server using Docker:

docker pull ja7adr/otp
docker run -p 8080:8080 ja7adr/otp

Image available at Docker Hub

🔬 Comparison

This comparison is performance and feature.

🚀 Performance Comparison

This comparison is for Ja7ad/otp vs pquerna/otp

Algorithm Suite Digits Library ns/op B/op allocs/op N (runs/sec)
SHA1 OCRA-1:HOTP-SHA1-6:QN08 6 Ja7ad/otp 1134 552 9 881,058
SHA1 HOTP/TOTP (default) 6 pquerna/otp 1420 592 13 704,225
SHA256 OCRA-1:HOTP-SHA256-8:C-QN08-PSHA1 8 Ja7ad/otp 984.3 592 9 1,015,907
SHA256 HOTP/TOTP (default) 8 pquerna/otp 1477 728 13 677,236
SHA512 OCRA-1:HOTP-SHA512-8:QN08-T1M 8 Ja7ad/otp 1752 944 9 570,853
SHA512 HOTP/TOTP (default) 8 pquerna/otp 2359 1224 13 423,778
Metric Ja7ad/otp pquerna/otp ✅ Winner
Execution time (ns/op) ~2x faster across all algorithms and digit sizes Slower in all cases Ja7ad/otp
Memory usage (B/op) ~30–50% less memory allocated Higher allocations Ja7ad/otp
Allocations (allocs/op) 7 allocations 13 allocations Ja7ad/otp
Dependencies Zero external deps Relies on stdlib + extras Ja7ad/otp
  • Ja7ad/otp: 736 ns, 520 B, 7 allocs
  • pquerna/otp: 1495 ns, 728 B, 13 allocs

✅ Feature Comparison

Feature Ja7ad/otp pquerna/otp
RFC 4226 HOTP
RFC 6238 TOTP
RFC 6287 OCRA
Built-in OCRA Suite Configs
Full RFC Test Vector Suite
Constant-Time Validation
Cross-platform Friendly
Zero Dependency Core ❌ (uses crypto/rand + external parsing)

📑 Algorithm (RFC)

📚 Usage

TOTP example
package main

import (
	"fmt"
	"github.com/Ja7ad/otp"
	"log"
	"time"
)

func main() {
	secret, err := otp.RandomSecret(otp.SHA1)
	if err != nil {
		log.Fatal(err)
	}

	t := time.Now()

	code, err := otp.GenerateTOTP(secret, t, otp.DefaultTOTPParam)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(code)

	ok, err := otp.ValidateTOTP(secret, code, t, otp.DefaultTOTPParam)
	if err != nil {
		log.Fatal(err)
	}

	if !ok {
		log.Fatal("Invalid OTP")
	}

	url, err := otp.GenerateTOTPURL(otp.URLParam{
		Issuer:      "https://example.com",
		Secret:      secret,
		AccountName: "foobar",
		Period:      otp.DefaultTOTPParam.Period,
		Digits:      otp.DefaultTOTPParam.Digits,
		Algorithm:   otp.DefaultTOTPParam.Algorithm,
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(url.String())
}
HOTP example code
package main

import (
	"fmt"
	"github.com/Ja7ad/otp"
	"log"
)

func main() {
	secret, err := otp.RandomSecret(otp.SHA1)
	if err != nil {
		log.Fatal(err)
	}

	counter := uint64(1)

	code, err := otp.GenerateHOTP(secret, counter, otp.DefaultHOTPParam)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(code)

	ok, err := otp.ValidateHOTP(secret, code, counter, otp.DefaultHOTPParam)
	if err != nil {
		log.Fatal(err)
	}

	if !ok {
		log.Fatal("Invalid OTP")
	}

	url, err := otp.GenerateHOTPURL(otp.URLParam{
		Issuer:      "https://example.com",
		Secret:      secret,
		AccountName: "foobar",
		Period:      otp.DefaultHOTPParam.Period,
		Digits:      otp.DefaultHOTPParam.Digits,
		Algorithm:   otp.DefaultHOTPParam.Algorithm,
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(url.String())
}
OCRA example code
package main

import (
	"fmt"
	"github.com/Ja7ad/otp"
)

func main() {
	secret, err := otp.RandomSecret(otp.SHA1)
	if err != nil {
		panic(err)
	}

	suite := otp.MustRawSuite("OCRA-1:HOTP-SHA1-6:QN08")

	code, err := otp.GenerateOCRA(secret, suite, otp.OCRAInput{
		Challenge: []byte("12345678"),
	})

	if err != nil {
		panic(err)
	}

	ok, err := otp.ValidateOCRA(secret, code, suite, otp.OCRAInput{
		Challenge: []byte("12345678"),
	})
	if err != nil {
		panic(err)
	}

	fmt.Println(ok)
}

🤝 Contributing

We welcome contributions of all kinds — from fixing bugs and improving documentation to implementing new RFCs.

Please read our Contributing Guide to get started. It includes setup instructions, coding standards, and development workflows.

Whether you're filing an issue, submitting a pull request, or suggesting an improvement — thank you for helping make this library better! 🙌

📖 References

About

A high-performance, zero-dependency Go package for generating and validating TOTP, HOTP and OCRA one-time passwords — RFC 4226, RFC 6238 and RFC 6287 compliant.

Topics

Resources

License

Stars

Watchers

Forks

Languages