htotp

package module
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: Apr 16, 2023 License: MIT Imports: 19 Imported by: 2

README

HTOTP - Golang One-Time Password, Two Factor Authentication

HTOTP is based on PyOTP. The underlying standards that this uses are RFC 4226 and RFC 6238.

It uses github.com/makiuchi-d/gozxing/qrcode for generation of QR Codes and extraction of URLs from QR codes. There are examples and tools that can do this wit the QR code as an image.

This library (or the library and tools) can be used for multi-factor authentication (MFA) or two-factor authentication (2FA). The tools include a command line authenticator (Similar to Google Authenticator) but it works at the command line.

MIT License

Library/Module Installation

$ go get gitlab.com/pschlump/htotp
$ go test 

If you want to use the tools (the command line authenticator for example) look in the ./tools directory. Each of them has its own README.md file and follow the build instructions for the tools. There is a Makefile for the top level that will compile and test all of the examples.

$ cd ./examples
$ make 
$ cd ../tools/ac
$ go build

Examples

Example 1 - Generate OTK

From ./example/gen-OTK.go an example of generating a one time passkey based on time:

package main

// Generate a time based One Time Passkey (OTP) given a secret.

import (
	"flag"
	"fmt"
	"os"

	"gitlab.com/pschlump/htotp"
)

// take the secret from the command line: not very secure - but this is ....... an example!
var Secret = flag.String("secret", "", "Use this as the SECRET")

func main() {
	flag.Parse()
	fns := flag.Args()

	if len(fns) != 0 {
		fmt.Fprintf(os.Stderr, "No additional arguments.\n")
		os.Exit(1)
	}

	if *Secret == "" {
		fmt.Fprintf(os.Stderr, "Must supply --secret 'secret' to generate a time based OTP\n")
		os.Exit(1)
	}

	fmt.Printf("Time based OTP is: %s\n", htotp.NewDefaultTOTP(*Secret).Now())
}
Example 2 - Generate

From ./example/gen-secret.go. Generate a "secret" that needs to be stored both on the server and in the authenticator.

package main

// Generate a suitable secret for HOTP and TOTP

import (
	"flag"
	"fmt"
	"os"

	"gitlab.com/pschlump/htotp"
)

var Length = flag.Int("lenght", 16, "Default length 6 for the secret")

func main() {
	if *Length <= 0 {
		fmt.Fprintf(os.Stderr, "Ivalid secret size, normally 16 long\n")
		os.Exit(1)
	}

	// func RandomSecret(length int) string {
	secret := htotp.RandomSecret(*Length)

	fmt.Printf("Secret: \"%s\"\n", secret)
}
Example 3 - Validate

Using the secret and the one time passkey (OTK) validate it.

Note: username is not needed for this because we are supplying the secret. Normally in a real system the username and authentication realm are used with a database to look up the secret. The "authenticator" client saves the "secret" locally.

package main

// Check that an OTP is correct(valid)


import (
	"flag"
	"fmt"
	"os"

	"gitlab.com/pschlump/htotp"
)

var OTP = flag.String("otp", "", "The one time password OTP")
var Secret = flag.String("secret", "", "Secret for this username and authentication realm.")

func main() {
	flag.Parse()
	fns := flag.Args()

	if len(fns) != 0 {
		fmt.Fprintf(os.Stderr, "Additional arguments not allowed.\n")
		os.Exit(1)
	}

	if *OTP == "" {
		fmt.Fprintf(os.Stderr, "Missing --otp 123456\n")
		os.Exit(1)
	}
	if *Secret == "" {
		fmt.Fprintf(os.Stderr, "Missing --secret 'xxxxxxxxxxx'\n")
		os.Exit(1)
	}

	if ok := CheckRfc6238TOTPKey("--not-needed--", *OTP, *Secret); ok {
		fmt.Printf("OTK is valid\n")
	} else {
		fmt.Printf("you may not pass....  Invalid OTK\n")
	}
}

// CheckRfc6238TOTPKey uses the `otp` and `secret` to check that the `otp` is valid.
func CheckRfc6238TOTPKey(un, otp, secret string) bool {
	totp := htotp.NewDefaultTOTP(secret)
	totp.Now()
	return totp.Verify(otp, htotp.CurrentTimestamp())
}
Example 4 - Generate URL for use in a QR Code

From ./example/gen-URL.go:

package main

import (
	"flag"
	"fmt"
	"os"

	"gitlab.com/pschlump/htotp"
)

var Un = flag.String("un", "example@www.2c-why.com", "Username for this user.  Default 'example@www.2c-why.com'.")
var Realm = flag.String("auth-realm", "www.2c-why.com", "Authenticaiton realm, defaults to www.2c-why.com")
var Secret = flag.String("secret", "", "Secret for this user, if '' (empty) then a secret will be generated and printed out")

func main() {
	flag.Parse()
	fns := flag.Args()

	if len(fns) != 0 {
		fmt.Fprintf(os.Stderr, "Additional arguments not allowed.\n")
		os.Exit(1)
	}

	if *Secret == "" {
		secret := htotp.RandomSecret(16)
		Secret = &secret
		if !*Raw {
			fmt.Printf("Generated Secret: %s\n", secret)
		}
	}

	u := GenerateURL(*Un, *Realm, *Secret)

	fmt.Printf("URL: %s\n", u)
}

func GenerateURL(un, realm, secret string) (otpURL string) {
	totp := htotp.NewDefaultTOTP(secret)
	otpURL = totp.ProvisioningUri(un, realm) // otpauth://totp/issuerName:demoAccountName?secret=4S62BZNFXXSZLCRO&issuer=issuerName
	return
}

MIT License

HTOTP is licensed under the MIT License

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BuildURI

func BuildURI(otpType OTPType, secret, accountName, issuerName, algorithm string, startCount, digits, period int) string

BuildURI returns the provisioning URI for the OTP. This works for both TOTP or HOTP. The URI is suitable for encoding in a QR-Code that can then be read by Google Authenticator or the command line ./tools/ac with the --import flag. The ./tools/ac can use the --importURI to import the string URI instead of parsing a QR code image.

See also:

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

Parameters:

otpType:     OTP type, must be one of { "totp", "hotp" }
secret:       The secret used to generate the URI
accountName:  Username - used on server to retretive the secret.
issuerName:   Name of the OTP issuer. This is the title of the OTP entry in Google Authenticator.
algorithm:    The algorithm used in the OTP generation, defauilt is "sha1" but other hashes can be used.
startCount:   For HOTP the starting counter value.  Ignored in TOTP.
digits:       The number of digits in the generated OTP.  Default 6. Pass 0 for the default.
period:       The number of seconds before the OTP expires.  Default 30.  Pass 0 for the default.

Returns: the provisioning URI

func CheckRfc4226TOTPKey

func CheckRfc4226TOTPKey(n int, otp, secret string) bool

func CheckRfc6238TOTPKey

func CheckRfc6238TOTPKey(un, otp, secret string) bool

CheckRfc6238TOTPKey uses the `otp` and `secret` to check that the `otp` is valid.

func CheckRfc6238TOTPKeyWithSkew

func CheckRfc6238TOTPKeyWithSkew(un, otp, secret string, skew, pskew uint) bool

func CurrentTimestamp

func CurrentTimestamp() int

CurrentTimestamp will return the current Unix timestamp.

func ExtractURIFromQRCodeImage

func ExtractURIFromQRCodeImage(fn string) (uri string, err error)

Extract URI from QR Code Image

func GenerateQRCodeFromURI

func GenerateQRCodeFromURI(uri, fn string)

Generate QR Code Image from URI

func GenerateRfc4226HOTPKey

func GenerateRfc4226HOTPKey(n int, secret string) (string, error)

func GenerateRfc6238TOTPKey

func GenerateRfc6238TOTPKey(un, secret string) string

func GenerateRfc6238TOTPKeyTL

func GenerateRfc6238TOTPKeyTL(un, secret string) (pin2fa string, tl uint)

func PadRight

func PadRight(str, pad string, lenght int) string

PadRight adds `pad` to `str` until reaching the `length`

func RandomSecret

func RandomSecret(length int) string

RandomSecret will generate a random secret of given length in a suitable format for use in either HOTP or TOTP.

Types

type HOTP

type HOTP struct {
	OTP
}

func NewDefaultHOTP

func NewDefaultHOTP(secret string) *HOTP

func NewHOTP

func NewHOTP(secret string, nDigits int, hasher *Hasher) *HOTP

func (*HOTP) At

func (hh *HOTP) At(count int) (rv string, err error)

Generates the OTP for the given count.

func (*HOTP) AtTL

func (hh *HOTP) AtTL(count int) (rv string, tl uint, err error)

func (*HOTP) ProvisioningUri

func (hh *HOTP) ProvisioningUri(un, realm string, cnt int) string

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.

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

Parameters:

	un 			Username - used on the server to lookup the secret.
 realm		Name of the issuer of the OTP
	cnt			Initial Count to start with

func (*HOTP) Verify

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

Verify will check if the `otp` that is passed is valid given the `count` and the Secret.

otp		The one time key that has been sent from the client
countr	The counter that is changing over time.

type Hasher

type Hasher struct {
	HashName string
	Digest   func() hash.Hash
}

From Copyright (c) 2018 xlzd, https://github.com/xlzd/gotp, MIT Licensed.

type OTP

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

From Copyright (c) 2018 xlzd, https://github.com/xlzd/gotp, MIT Licensed.

func NewOTP

func NewOTP(secret string, nDigits int, hasher *Hasher) OTP

From Copyright (c) 2018 xlzd, https://github.com/xlzd/gotp, MIT Licensed.

func (*OTP) ConvertSecretToBytes

func (oo *OTP) ConvertSecretToBytes() (bytes []byte, err error)

From Copyright (c) 2018 xlzd, https://github.com/xlzd/gotp, MIT Licensed.

func (*OTP) GenerateOTP

func (oo *OTP) GenerateOTP(input uint) (string, error)

func (*OTP) GenerateOTPTL

func (oo *OTP) GenerateOTPTL(input, tr uint) (string, uint, error)

type OTPType

type OTPType int
const (
	OtpTypeTotp OTPType = 1
	OtpTypeHotp OTPType = 2
)

func (OTPType) String

func (xx OTPType) String() string

type TOTP

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

time-based OTP, TOTP - uses the counter as a time window.

func NewDefaultTOTP

func NewDefaultTOTP(secret string) *TOTP

NewDefaultTOTP returns a TOTP with default setup. 6 digits and a time window of 30 seconds. The hashis the default hashing algorythm.

func NewTOTP

func NewTOTP(secret string, nDigits, timeWindow int, hasher *Hasher) *TOTP

func (*TOTP) At

func (tt *TOTP) At(timestamp int) string

Generate time OTP of given timestamp

func (*TOTP) AtTL

func (tt *TOTP) AtTL(timestamp int) (pin2fa string, tr uint)

Generate time OTP of given timestamp

func (*TOTP) GenerateTOTPAndSecondsLeft

func (tt *TOTP) GenerateTOTPAndSecondsLeft() (otp string, timeLeft int)

GenerateTOTPAndSecondsLeft Generate the current time OTP and number of seconds left in window. This is useful for a count-down display of how long the OTP is valid.

func (TOTP) GetTimeWindow

func (tt TOTP) GetTimeWindow() int

func (*TOTP) Now

func (tt *TOTP) Now() string

Generate the current time OTP

func (*TOTP) ProvisioningUri

func (tt *TOTP) ProvisioningUri(un, realm string) string

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.

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

Parameters:

	un 			Username - used on the server to lookup the secret.
 realm		Name of the issuer of the OTP

func (*TOTP) SetSkew

func (tt *TOTP) SetSkew(skew, pskew uint)

xyzzy -test-

func (*TOTP) Verify

func (tt *TOTP) Verify(otp string) bool

Verify checks the OTP at the current timestamp

func (*TOTP) VerifyAtTimestamp

func (tt *TOTP) VerifyAtTimestamp(otp string, timestamp int) bool

VerifyAtTimestamp checks the OTP at a given timestamp

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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