SkillAgentSearch skills...

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.

Install / Use

/learn @ja7ad/Otp
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

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

<details><summary>TOTP example</summary>
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())
}
</details> <details><summary>HOTP example code</summary>
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())
}
</details> <details><summary>OCRA example code</summary>
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)
}
</details>

🤝 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

Related Skills

View on GitHub
GitHub Stars38
CategoryDevelopment
Updated1mo ago
Forks1

Languages

Go

Security Score

95/100

Audited on Feb 10, 2026

No findings