gotp

package module
v2.0.4 Latest Latest
Warning

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

Go to latest
Published: Feb 27, 2020 License: MIT Imports: 12 Imported by: 8

README

GOTP - The Golang One-Time Password Library

build-status go-report-card codecov go.dev release mit-license

GOTP is a Golang package for generating and verifying one-time passwords. It can be used to implement two-factor (2FA) or multi-factor (MFA) authentication methods in anywhere that requires users to log in.

Open MFA standards are defined in RFC 4226 (HOTP: An HMAC-Based One-Time Password Algorithm) and in RFC 6238 (TOTP: Time-Based One-Time Password Algorithm). GOTP implements server-side support for both of these standards.

This is a fork of xlzd/gotp

gotp-gopher

Installation

go get github.com/diebietse/gotp/v2

Usage

Check API docs at https://pkg.go.dev/github.com/diebietse/gotp/v2

Time-based OTPs
secret, _ := gotp.DecodeBase32("4S62BZNFXXSZLCRO")
totp, _ := gotp.NewTOTP(secret)
totp.Now()  // current otp '123456'
totp.At(1524486261)  // otp of timestamp 1524486261 '123456'

// OTP verified for a given timestamp
totp.Verify("492039", 1524486261)  // true
totp.Verify("492039", 1520000000)  // false

// generate a provisioning uri
totp.ProvisioningURI("demoAccountName", "issuerName")
// otpauth://totp/issuerName:demoAccountName?secret=4S62BZNFXXSZLCRO&issuer=issuerName
Counter-based OTPs
secret, _ := gotp.DecodeBase32("4S62BZNFXXSZLCRO")
hotp, _ := gotp.NewHOTP(secret)
hotp.At(0)  // '944181'
hotp.At(1)  // '770975'

// OTP verified for a given counter
hotp.Verify("944181", 0)  // true
hotp.Verify("944181", 1)  // false

// generate a provisioning uri
hotp.ProvisioningURI("demoAccountName", "issuerName", 1)
// otpauth://hotp/issuerName:demoAccountName?secret=4S62BZNFXXSZLCRO&counter=1&issuer=issuerName
Hex HOTP Output Example
secret, _ := gotp.DecodeBase32("4S62BZNFXXSZLCRO")
hotp, _ := gotp.NewHOTP(secret, FormatHex())
hotp.At(0)  // '0e6835'
hotp.At(1)  // '0bc39f'

// OTP verified for a given timestamp
hotp.Verify("0e6835", 0)  // true
hotp.Verify("0e6835", 1)  // false
Generate random secret
secret, _ := RandomSecret(sha1.Size)
Google Authenticator Compatible

GOTP works with the Google Authenticator iPhone and Android app, as well as other OTP apps like Authy. GOTP includes the ability to generate provisioning URIs for use with the QR Code scanner built into these MFA client apps via otpObj.ProvisioningUri method:

secret, _ := gotp.DecodeBase32("4S62BZNFXXSZLCRO")
totp, _ := gotp.NewTOTP(secret)
totp.ProvisioningUri("demoAccountName", "issuerName")
// otpauth://totp/issuerName:demoAccountName?secret=4S62BZNFXXSZLCRO&issuer=issuerName

secret, _ := gotp.DecodeBase32("4S62BZNFXXSZLCRO")
hotp, _ := gotp.NewHOTP(secret)
hotp.ProvisioningUri("demoAccountName", "issuerName", 1)
// otpauth://hotp/issuerName:demoAccountName?secret=4S62BZNFXXSZLCRO&counter=1&issuer=issuerName

This URL can then be rendered as a QR Code which can then be scanned and added to the users list of OTP credentials.

Working example

Scan the following barcode with your phone's OTP app (e.g. Google Authenticator):

Demo

Now run the following and compare the output:

package main

import (
	"fmt"
	gotp "github.com/diebietse/gotp/v2"
)

func main() {
	secret, _ := gotp.DecodeBase32("4S62BZNFXXSZLCRO")
	totp, _ := gotp.NewTOTP(secret)
	fmt.Println("Current OTP is", totp.Now())
}

License

GOTP is licensed under the MIT License

Documentation

Overview

Package gotp is a package for generating and verifying one-time passwords.

It can be used to implement two-factor (2FA) or multi-factor (MFA) authentication methods anywhere that requires users to log in.

Open MFA standards are defined in RFC 4226 (HOTP: An HMAC-Based One-Time Password Algorithm) and in RFC 6238 (TOTP: Time-Based One-Time Password Algorithm). GOTP implements server-side support for both of these standards.

This is a fork of xlzd/gotp

Index

Examples

Constants

View Source
const MaxOTPLength = 8

MaxOTPLength is the maximun character length that OTP can be set to in the library

Variables

This section is empty.

Functions

func DecodeBase32

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

DecodeBase32 decodes a base32 string and returns a byte array or error if it is not a valid base32 string

func EncodeBase32

func EncodeBase32(secret []byte) string

EncodeBase32 encodes a byte array into a base32 string

func RandomSecret

func RandomSecret(length int) ([]byte, error)

RandomSecret generate a random []byte secret of given length

Example
secret, err := RandomSecret(sha1.Size)
if err != nil {
	panic(err)
}
fmt.Printf("secret length is: %d\n", len(secret))
Output:

secret length is: 20

Types

type HOTP

type HOTP struct {
	// contains filtered or unexported fields
}

HOTP generates usage counter based OTPs

func NewHOTP

func NewHOTP(secret []byte, opt ...OTPOption) (*HOTP, error)

NewHOTP returns a HOTP struct with the given secret and set defaults. The digit count is 6, hasher SHA1 and format is decimal output.

Example
secret, err := DecodeBase32("4S62BZNFXXSZLCRO")
if err != nil {
	panic(err)
}
otp, err := NewHOTP(secret)
if err != nil {
	panic(err)
}

otpAt, err := otp.At(0)
if err != nil {
	panic(err)
}
fmt.Printf("one-time password of counter 0 is: %v\n", otpAt)
uri, err := otp.ProvisioningURI("demoAccountName", "issuerName", 1)
if err != nil {
	panic(err)
}
fmt.Printf("uri: %s\n", uri)

valid, err := otp.Verify("944181", 0)
if err != nil {
	panic(err)
}
fmt.Printf("otp is valid: %v\n", valid)
Output:

one-time password of counter 0 is: 944181
uri: otpauth://hotp/issuerName:demoAccountName?secret=4S62BZNFXXSZLCRO&counter=1&issuer=issuerName
otp is valid: true

func (*HOTP) At

func (h *HOTP) At(count int) (string, error)

At generates the OTP for the given `count` offset.

func (*HOTP) ProvisioningURI

func (h *HOTP) ProvisioningURI(accountName, issuerName string, initialCount int) (string, error)

ProvisioningURI returns the provisioning URI for the OTP. This can then be encoded in a QR Code and used to provision an OTP app like Google Authenticator.

It can be given a human readable "accountName" and "issuerName", as well as an "initialCount" for the OTP generation.

See https://github.com/google/google-authenticator/wiki/Key-Uri-Format.

func (*HOTP) Verify

func (h *HOTP) Verify(otp string, count int) (bool, error)

Verify verifies if a given OTP is valid at a given `count` offset

type Hasher

type Hasher struct {
	// HashName is unique identifier for this hashing implementation
	HashName string
	// Digest is a function that returns a `hash.Hash` when called
	Digest func() hash.Hash
}

Hasher provides a custom hashing implementation for a OTP

type OTPOption

type OTPOption func(*otpOptions) error

OTPOption configures OTPs

func FormatHex

func FormatHex() OTPOption

FormatHex lets OTPs be returned in Hexadecimal format instead of Decimal format

func WithHasher

func WithHasher(hasher *Hasher) OTPOption

WithHasher lets OTPs be generated using the given hasher

func WithInterval

func WithInterval(i int) OTPOption

WithInterval lets TOTPs have the given interval for changing its values

func WithLength

func WithLength(l int) OTPOption

WithLength make generated OTPs have the given length

type TOTP

type TOTP struct {
	// contains filtered or unexported fields
}

TOTP generates time-based OTPs

func NewTOTP

func NewTOTP(secret []byte, opt ...OTPOption) (*TOTP, error)

NewTOTP returns a TOTP struct with the given secret and set defaults. The digit count is 6, interval 30, hasher SHA1 and format is decimal output.

Example
secret, err := DecodeBase32("4S62BZNFXXSZLCRO")
if err != nil {
	panic(err)
}
otp, err := NewTOTP(secret)
if err != nil {
	panic(err)
}

otpAt, err := otp.At(0)
if err != nil {
	panic(err)
}
fmt.Printf("one-time password of timestamp 0 is: %v\n", otpAt)
uri, err := otp.ProvisioningURI("demoAccountName", "issuerName")
if err != nil {
	panic(err)
}
fmt.Printf("uri: %s\n", uri)

valid, err := otp.Verify("179394", 1524485781)
if err != nil {
	panic(err)
}
fmt.Printf("otp is valid: %v\n", valid)
Output:

one-time password of timestamp 0 is: 944181
uri: otpauth://totp/issuerName:demoAccountName?secret=4S62BZNFXXSZLCRO&issuer=issuerName
otp is valid: true

func (*TOTP) At

func (t *TOTP) At(timestamp int) (string, error)

At generates the time-based OTP for the given timestamp.

func (*TOTP) Now

func (t *TOTP) Now() (string, error)

Now generates the current time-based OTP.

Example
secret, err := DecodeBase32("4S62BZNFXXSZLCRO")
if err != nil {
	panic(err)
}
otp, err := NewTOTP(secret)
if err != nil {
	panic(err)
}
currentOTP, err := otp.Now()
if err != nil {
	panic(err)
}
fmt.Printf("current one-time password is: %v\n", currentOTP)
Output:

func (*TOTP) NowWithExpiration

func (t *TOTP) NowWithExpiration() (string, int64, error)

NowWithExpiration generates the current time-based OTP and expiration time.

func (*TOTP) ProvisioningURI

func (t *TOTP) ProvisioningURI(accountName, issuerName string) (string, error)

ProvisioningURI returns the provisioning URI for the TOTP. This can then be encoded in a QR Code and used to provision an OTP app like Google Authenticator.

It can be given a human readable "accountName" and "issuerName" for the TOTP generation.

See https://github.com/google/google-authenticator/wiki/Key-Uri-Format.

func (*TOTP) Verify

func (t *TOTP) Verify(otp string, timestamp int) (bool, error)

Verify verifies if a given OTP is valid at a given timestamp

Jump to

Keyboard shortcuts

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