otp

package module
v1.3.3 Latest Latest
Warning

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

Go to latest
Published: Apr 29, 2025 License: MIT Imports: 19 Imported by: 0

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

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

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrUnsupportedAlgorithm = errors.New("unsupported algorithm")
	ErrInvalidCodeLength    = errors.New("invalid code length")
	ErrInvalidCode          = errors.New("invalid otp code")
	ErrIssuerRequired       = errors.New("issuer is required")
	ErrAccountNameRequired  = errors.New("account name is required")
	ErrSecretRequired       = errors.New("secret is required")
	ErrInvalidSkew          = errors.New("invalid skew, a larger Skew increases the chance of a brute-force hit")
	ErrInvalidRawSuite      = errors.New("invalid OCRA suite string")
)
View Source
var DefaultHOTPParam = &Param{
	Digits:    SixDigits,
	Algorithm: SHA1,
	Period:    0,
	Skew:      2,
}

DefaultHOTPParam provides a default configuration for HOTP generation and validation, using SHA1, 6 digits. Note: Period and Skew are not used in HOTP but included for Param compatibility.

View Source
var DefaultTOTPParam = &Param{
	Digits:    SixDigits,
	Period:    30,
	Skew:      0,
	Algorithm: SHA1,
}

DefaultTOTPParam provides a default configuration for TOTP generation and validation, using SHA1, 6 digits, a 30-second period, and zero skew.

View Source
var TimeCounterFunc = func(t time.Time, period uint) uint64 {
	return uint64(t.Unix()) / uint64(period)
}

TimeCounterFunc returns the TOTP counter value based on the Unix time and period. It performs integer division of time by the period to produce a moving counter window.

Functions

func DecodeSecret

func DecodeSecret(secret string) ([]byte, error)

func GenerateHOTP

func GenerateHOTP(secret string, counter uint64, param *Param) (string, error)

GenerateHOTP generates an HOTP code from a given secret and counter. If `param` is nil, DefaultHOTPParam is used.

Example
package main

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

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

	code, err := otp.GenerateHOTP(secret, 1, otp.DefaultHOTPParam)
	if err != nil {
		panic(err)
	}

	fmt.Println(code)
}

func GenerateHOTPURL

func GenerateHOTPURL(param URLParam) (*url.URL, error)

GenerateHOTPURL constructs an otpauth:// URL for configuring HOTP-based authenticators. The URL includes the issuer, account name, secret, algorithm, digits, and counter.

Example output: otpauth://hotp/Example:alice@domain.com?secret=BASE32ENCODEDSECRET&issuer=Example&algorithm=SHA1&digits=6&counter=0

Example
package main

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

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

	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 {
		panic(err)
	}

	fmt.Println(url.String())
}

func GenerateOCRA

func GenerateOCRA(secret string, suite Suite, input OCRAInput) (string, error)

GenerateOCRA generates a one-time password (OTP) using the OCRA algorithm (RFC 6287) for the given secret, suite, and input parameters.

The `secret` should be a base32-encoded shared key (e.g., from provisioning), and will be decoded into its raw binary form. The `suite` defines the OCRA configuration (create Suite using NewSuite or NewRawSuite). The `input` provides dynamic values like challenge, counter, session data, timestamp, etc.

Returns the generated OTP string or an error if decoding the secret or derivation fails.

Example
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)
	}

	fmt.Println(code)
}

func GenerateTOTP

func GenerateTOTP(secret string, t time.Time, param *Param) (string, error)

GenerateTOTP generates a TOTP code based on the given secret and timestamp. If param is nil, DefaultTOTPParam is used. The secret must be encoded according to the specified algorithm's encoding (e.g., base32 for SHA1).

Example
package main

import (
	"fmt"
	"github.com/ja7ad/otp"
	"time"
)

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

	code, err := otp.GenerateTOTP(secret, time.Now(), otp.DefaultTOTPParam)
	if err != nil {
		panic(err)
	}

	fmt.Println(code)
}

func GenerateTOTPURL

func GenerateTOTPURL(param URLParam) (*url.URL, error)

GenerateTOTPURL constructs an otpauth:// URL for configuring TOTP-based authenticators (e.g., Google Authenticator). The URL includes the issuer, account name, secret, and TOTP parameters.

Example output: otpauth://totp/Example:alice@domain.com?secret=BASE32ENCODEDSECRET&issuer=Example&algorithm=SHA1&digits=6&period=30

Example
package main

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

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

	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 {
		panic(err)
	}

	fmt.Println(url.String())
}

func IsKnownSuite

func IsKnownSuite(raw string) bool

IsKnownSuite reports whether the given raw OCRA suite string is registered in the internal list of known supported suite configurations.

This is useful for validating user input or performing discovery checks.

Example:

if !IsKnownSuite(input) {
    return fmt.Errorf("unsupported OCRA suite")
}

func LeftPadHex

func LeftPadHex(s string, totalLen int) string

LeftPadHex returns the input hex string left-padded with '0' characters until it reaches totalLen characters. If the input string is longer than or equal to totalLen, it returns the rightmost totalLen characters.

func ListSuites

func ListSuites() []string

ListSuites returns all registered and supported OCRA raw suite strings. This is useful for introspection, documentation, CLI display, or API discovery.

func MustHexPadLeft

func MustHexPadLeft(hexStr string, size int) []byte

MustHexPadLeft decodes a hex string after left-padding it to the desired byte length. The size parameter specifies the desired number of bytes; the function left-pads the hex string to size*2 characters. It panics if the hex decoding fails.

func ParseDecimal64BigEndian

func ParseDecimal64BigEndian(decStr string) ([]byte, error)

ParseDecimal64BigEndian is similar to ParseDecimalToBigEndian8. It converts a decimal string to an 8-byte big-endian slice by parsing the string as a uint64.

func ParseDecimalChallengeRFC6287

func ParseDecimalChallengeRFC6287(s string) ([]byte, error)

ParseDecimalChallengeRFC6287 converts a decimal challenge string to a 128-byte value as required by RFC 6287. The conversion process is as follows:

  1. Parse the input as a decimal number into a big.Int.
  2. Convert the number to an uppercase hexadecimal string.
  3. Right-pad the hex string with '0's until its length is 256 characters (which corresponds to 128 bytes).
  4. Decode the padded hex string into a byte slice.

func ParseDecimalToBigEndian8

func ParseDecimalToBigEndian8(s string) ([]byte, error)

ParseDecimalToBigEndian8 converts a decimal string to an 8-byte big-endian representation. It interprets the input string as a base-10 unsigned integer, then returns an 8-byte slice where the most-significant byte is at index 0. This is useful for encoding counters or similar values.

func ParseHexTimestamp

func ParseHexTimestamp(ts string) ([]byte, error)

ParseHexTimestamp converts a hex-encoded timestamp string to an 8-byte slice. It left-pads the input string with '0' characters until it is 16 hex digits long (i.e. 8 bytes), then decodes the padded string.

func RandomSecret

func RandomSecret(algo Algorithm) (string, error)

RandomSecret returns a base32-encoded random secret for the given algorithm. The secret is of appropriate byte length for RFC-compliant HOTP/TOTP implementations.

func To8ByteBigEndian

func To8ByteBigEndian(v uint64) []byte

To8ByteBigEndian converts a uint64 value into an 8-byte big-endian slice. This is a convenience function for encoding counters or time values.

func ValidateHOTP

func ValidateHOTP(secret, code string, counter uint64, param *Param) (bool, error)

ValidateHOTP checks whether the given HOTP code is valid for the provided secret and counter. Returns true if valid, false otherwise. Uses constant-time comparison internally. If `param` is nil, DefaultHOTPParam is used.

Example
package main

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

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

	counter := uint64(1)

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

	fmt.Println(code)

	ok, err := otp.ValidateHOTP(secret, code, counter, otp.DefaultTOTPParam)
	if err != nil {
		panic(err)
	}

	fmt.Println(ok)
}

func ValidateOCRA

func ValidateOCRA(secret, code string, suite Suite, input OCRAInput) (bool, error)

ValidateOCRA checks whether a provided OTP code is valid for the given OCRA suite, secret, and input parameters.

The `secret` should be a base32-encoded key, which is decoded before validation. The `code` is the OTP to validate (usually entered by the user). The `suite` and `input` define the expected context in which the OTP should have been generated.

Returns true if the code is valid, false otherwise. An error is returned if decoding the secret fails or if validation encounters a critical issue.

Example
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(code, secret, suite, otp.OCRAInput{
		Challenge: []byte("12345678"),
	})
	if err != nil {
		panic(err)
	}

	fmt.Println(ok)
}

func ValidateTOTP

func ValidateTOTP(secret, code string, t time.Time, param *Param) (bool, error)

ValidateTOTP checks whether the given TOTP code is valid for the specified time and secret. It uses constant-time comparison to avoid timing attacks. Returns true if the code is valid, false otherwise. If param is nil, DefaultTOTPParam is used.

Example
package main

import (
	"fmt"
	"github.com/ja7ad/otp"
	"time"
)

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

	t := time.Now()

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

	fmt.Println(code)

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

	fmt.Println(ok)
}

Types

type Algorithm

type Algorithm uint8

Algorithm defines the hashing algorithm used in the HMAC function. Supported values are SHA1, SHA256, and SHA512, per RFC 6238.

const (
	// SHA1 is the default algorithm used in TOTP/HOTP (RFC 4226, RFC 6238).
	SHA1 Algorithm = iota

	// SHA256 offers stronger security than SHA1 with better resistance to collisions.
	SHA256

	// SHA512 is the strongest hash option supported, useful in high-security environments.
	SHA512
)

func AlgorithmFromStr

func AlgorithmFromStr(algo string) Algorithm

func (Algorithm) String

func (algo Algorithm) String() string

type ChallengeFormat

type ChallengeFormat int

ChallengeFormat enumerates the possible challenge formats.

const (
	ChallengeNone      ChallengeFormat = iota
	ChallengeNumeric08                 // QN08 → typically 8-digit numeric
	ChallengeNumeric10                 // QN10 → typically 10-digit numeric
	ChallengeAlpha08                   // QA08 → typically 8-char alphanumeric
	ChallengeAlpha10                   // QA10 → typically 10-char alphanumeric
	ChallengeHex08                     // QH08 → typically 8-hex-digit challenge
	ChallengeHex10                     // QH10 → typically 10-hex-digit challenge
)

type Digits

type Digits uint8
const (
	SixDigits   Digits = 6
	EightDigits Digits = 8
	NineDigits  Digits = 9
	TenDigits   Digits = 10
)

func DigitsFromStr

func DigitsFromStr(digits string) Digits

func (Digits) Int

func (d Digits) Int() int

type OCRAInput

type OCRAInput struct {
	// Counter is an 8-byte big-endian value, used if the suite has IncludeCounter=true.
	// Typically incremented for each new OCRA calculation.
	Counter []byte

	// Challenge is the raw challenge data. If the suite has IncludeChallenge=true,
	// we typically expect a certain minimum length (8 or 10 bytes) and a max of 128 bytes.
	Challenge []byte

	// Password is the hashed PIN or passphrase. If the suite has IncludePassword=true
	// and the suite's PasswordHash is e.g. PasswordSHA1, we expect the length to match
	// that hash (20 bytes for SHA-1, etc.).
	Password []byte

	// SessionInfo is optional user or system data (e.g. channel binding info), up to 128 bytes.
	// Only used if IncludeSession=true.
	SessionInfo []byte

	// Timestamp is an 8-byte big-endian representation of the time-step
	// if IncludeTimestamp=true (e.g. for T1M). Must be exactly 8 bytes.
	Timestamp []byte
}

OCRAInput holds the raw data that will be concatenated (in the order defined by RFC6287) for the HMAC computation. Which fields are required or optional depends on the SuiteConfig (cfg).

func HexInputToOCRA

func HexInputToOCRA(counter, challenge, password, sessionInfo, timestamp string) (OCRAInput, error)

func (OCRAInput) Validate

func (in OCRAInput) Validate(cfg SuiteConfig) error

Validate checks that the input matches the suite's requirements.

type Param

type Param struct {
	// Digits is the length of the generated OTP code (commonly 6 or 8).
	Digits Digits

	// Period is the time step in seconds for TOTP (e.g., 30 means OTP changes every 30s).
	// This is not used in HOTP.
	Period uint

	// Skew is the allowed number of time steps (forward/backward) during TOTP validation
	// to account for clock drift between client and server.
	// security: A larger Skew increases the chance of a brute-force hit, max 10.
	// default for hotp is 2
	Skew uint

	// Algorithm specifies which HMAC hashing algorithm to use (SHA1, SHA256, SHA512).
	Algorithm Algorithm
}

Param defines configuration parameters for generating and validating OTPs.

type PasswordHashAlgorithm

type PasswordHashAlgorithm int

PasswordHashAlgorithm enumerates the possible PIN/password hash types.

const (
	PasswordNone PasswordHashAlgorithm = iota
	PasswordSHA1
	PasswordSHA256
	PasswordSHA512
)

type RawSuite

type RawSuite struct {
	SuiteConfig
}

func MustRawSuite

func MustRawSuite(raw string) RawSuite

MustRawSuite is like NewRawSuite but panics if the suite string is invalid or unknown. It is intended for use in tests, internal registrations, or hardcoded trusted values where failure is not expected.

Example:

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

func (RawSuite) Config

func (r RawSuite) Config() SuiteConfig

func (RawSuite) String

func (r RawSuite) String() string

func (RawSuite) Validate

func (r RawSuite) Validate() error

type Suite

type Suite interface {
	// Config returns the parsed SuiteConfig from this suite.
	Config() SuiteConfig

	// String returns the raw string representation of the suite.
	String() string

	// Validate checks whether the suite definition is structurally and semantically valid.
	// For RawSuite, this includes parsing the raw string and validating the resulting SuiteConfig.
	// For SuiteConfig, this includes checks on hash algorithm, digit length, challenge format,
	// and consistency of included input fields.
	//
	// It returns an error if the suite is invalid or unsupported.
	Validate() error
}

Suite represents an abstract OCRA suite definition. It allows OCRA-related functions to accept either a raw string suite (e.g., "OCRA-1:HOTP-SHA1-6:QN08") or a pre-parsed SuiteConfig.

The Config method parses and returns the normalized SuiteConfig structure, enabling efficient downstream processing. The String method returns the original suite string representation.

This interface enables both ergonomic and performance-friendly usage:

DeriveOCRA(RawSuite("OCRA-1:HOTP-SHA1-6:QN08"), secret, inputs)
DeriveOCRA(ParsedSuite{...}, secret, inputs)

All internal logic operates on SuiteConfig, regardless of input type.

func NewRawSuite

func NewRawSuite(raw string) (Suite, error)

NewRawSuite returns a Suite instance based on a raw OCRA suite string. It looks up the string in the list of known, pre-registered suite definitions and returns the corresponding RawSuite.

If the raw suite string is not found or the associated SuiteConfig is invalid, it returns an appropriate error.

This is the recommended way to safely create a Suite from raw input at runtime. You can find raw suites by ListSuites function.

func NewSuite

func NewSuite(cfg SuiteConfig) (Suite, error)

NewSuite returns a validated Suite implementation from a given SuiteConfig. The function first verifies the internal consistency of the config via Validate(), then ensures that the SuiteConfig corresponds to a known, pre-registered raw suite.

If the suite is valid and known, it returns a Suite interface that can be used for deriving OCRA codes. Otherwise, it returns an appropriate error.

This method is useful for users who construct SuiteConfig programmatically but still want strict adherence to known suite definitions.

Returns ErrInvalidRawSuite if the suite's Raw field is not registered.

type SuiteConfig

type SuiteConfig struct {
	// Original suite string (useful for logging, debug, etc.)
	Raw string `json:"-"`

	// OTP parameters
	Hash   Algorithm `json:"hash"`   // SHA1, SHA256, SHA512
	Digits int       `json:"digits"` // OTP digits: 6, 7, 8

	// Challenge type
	Challenge ChallengeFormat `json:"challenge"` // QN08, QA10, etc.

	// Input flags (used to determine which inputs are expected)
	IncludeCounter   bool `json:"include_counter,omitempty"`   // C
	IncludeChallenge bool `json:"include_challenge,omitempty"` // Q (always true if Challenge is set)
	IncludePassword  bool `json:"include_password,omitempty"`  // P
	IncludeSession   bool `json:"include_session,omitempty"`   // S
	IncludeTimestamp bool `json:"include_timestamp,omitempty"` // T

	// Extra metadata
	PasswordHash PasswordHashAlgorithm `json:"password_hash"`       // PSHA1, PSHA256, PSHA512 (optional)
	TimeStep     int                   `json:"time_step,omitempty"` // T1, T2, etc. (in seconds; 0 = not used)
}

func SuiteConfigFromRaws

func SuiteConfigFromRaws(rawSuite string) SuiteConfig

func (SuiteConfig) Config

func (cfg SuiteConfig) Config() SuiteConfig

func (SuiteConfig) String

func (cfg SuiteConfig) String() string

func (SuiteConfig) Validate

func (cfg SuiteConfig) Validate() error

type URLParam

type URLParam struct {
	// Name of the issuing Organization/Company.
	Issuer string
	// Name of the User's Account (eg, email address)
	AccountName string
	// Number of seconds a TOTP hash is valid for. Defaults to 30 seconds.
	Period uint
	// Secret to store. Defaults to a randomly generated secret of SecretSize.  You should generally leave this empty.
	Secret string
	// Digits to request. Defaults to 6.
	Digits Digits
	// Algorithm to use for HMAC. Defaults to SHA1.
	Algorithm Algorithm
}

func ParseOTPAuthURL

func ParseOTPAuthURL(u *url.URL) (*URLParam, error)

ParseOTPAuthURL parses an otpauth:// URL (TOTP or HOTP) and converts it into a URLParam struct. Supported format: otpauth://TYPE/LABEL?secret=...&issuer=...&digits=...&algorithm=...&period=...

Directories

Path Synopsis
_example
hotp command
ocra command
totp command

Jump to

Keyboard shortcuts

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