totp

package
v0.0.1-alpha Latest Latest
Warning

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

Go to latest
Published: Sep 5, 2022 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Example
package main

import (
	"fmt"
	"log"

	"github.com/KEINOS/go-totp/totp"
)

func main() {
	// Generate a new secret key
	Issuer := "Example.com"
	AccountName := "alice@example.com"

	key, err := totp.GenerateKey(Issuer, AccountName)
	if err != nil {
		log.Fatal(err)
	}

	// Generate 6 digits passcode (valid for 30 seconds)
	passcode, err := key.PassCode()
	if err != nil {
		log.Fatal(err)
	}

	// Validate the passcode
	valid, err := key.Validate(passcode)
	if err != nil {
		log.Fatal(err)
	}

	if valid {
		fmt.Println("Passcode is valid")
	}

}
Output:

Passcode is valid
Example (Advanced)
package main

import (
	"fmt"
	"log"

	"github.com/KEINOS/go-totp/totp"
)

func main() {
	// Options to generate a new key. The secret will be generated randomly.
	opts := totp.Options{
		Issuer:      "Example.com",
		AccountName: "alice@example.com",
		Algorithm:   totp.Algorithm("SHA1"), // Choices are: MD5, SHA1, SHA256 and SHA512
		Period:      60,                     // Validity period in seconds
		SecretSize:  20,                     // Secret key size in bytes
		Skew:        0,                      // Number of periods before or after the current time to allow.
		Digits:      totp.Digits(8),         // Choices are: 6 and 8
	}

	// Generate a new secret key
	key, err := totp.GenerateKeyCustom(opts)
	if err != nil {
		log.Fatal(err)
	}

	// Generate 8 digits passcode that are valid for 60 seconds (see options above)
	passcode, err := key.PassCode()
	if err != nil {
		log.Fatal(err)
	}

	// Validate the passcode
	valid, err := key.Validate(passcode)
	if err != nil {
		log.Fatal(err)
	}

	if valid {
		fmt.Println("Passcode is valid")
	}

}
Output:

Passcode is valid

Index

Examples

Constants

View Source
const (
	OptionAlgorithmDefault  = Algorithm("SHA512") // Default algorithm
	OptionPeriodDefault     = uint(30)            // Seconds (rfc6238)
	OptionSecretSizeDefault = uint(20)            // Bytes
	OptionSkewDefault       = uint(0)             // Periods
	OptionDigitsDefault     = Digits(6)           // Six digits
)

Constants for the default values of the options.

Variables

This section is empty.

Functions

This section is empty.

Types

type Algorithm

type Algorithm string

Algorithm is a string that represents the algorithm used for HMAC.

Example
package main

import (
	"fmt"
	"log"

	"github.com/KEINOS/go-totp/totp"
)

func main() {
	// Create a new Algorithm object from a string. Choices are:
	//   MD5, SHA1, SHA256 and SHA512.
	algo, err := totp.NewAlgorithmStr("SHA512")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Algorithm:", algo.String())
	fmt.Println("Algorithm ID:", algo.ID())
	fmt.Printf("Type: %T\n", algo.OTPAlgorithm())

}
Output:

Algorithm: SHA512
Algorithm ID: 2
Type: otp.Algorithm

func NewAlgorithmID

func NewAlgorithmID(algoID int) (Algorithm, error)

NewAlgorithmID creates a new Algorithm object from an int.

func NewAlgorithmStr

func NewAlgorithmStr(algo string) (Algorithm, error)

NewAlgorithmStr creates a new Algorithm object from a string. Choices are: MD5, SHA1, SHA256 and SHA512.

func (Algorithm) ID

func (algo Algorithm) ID() int

ID returns the ID of the algorithm which is the same int value as the original OTP library.

Undefined ID will always return 2 (SHA512).

func (Algorithm) IsSupported

func (algo Algorithm) IsSupported() bool

IsSupported returns true if the algorithm is supported.

Example
package main

import (
	"fmt"

	"github.com/KEINOS/go-totp/totp"
)

func main() {
	// Cast a string to Algorithm type
	algo := totp.Algorithm("BLAKE3")

	// Check if the algorithm is supported
	if algo.IsSupported() {
		fmt.Println("Algorithm is supported")
	} else {
		fmt.Println("Algorithm is not supported")
	}

}
Output:

Algorithm is not supported

func (Algorithm) OTPAlgorithm

func (algo Algorithm) OTPAlgorithm() otp.Algorithm

OTPAlgorithm is similar to ID() but returns in the original type of the OTP library.

Undefined Algorithm type will always return `otp.AlgorithmSHA512`.

func (Algorithm) String

func (algo Algorithm) String() string

String is an implementation of the Stringer interface.

type Digits

type Digits uint

Digits represents the number of digits present in the user's OTP passcode. Six and Eight are the most common values.

Example
package main

import (
	"fmt"

	"github.com/KEINOS/go-totp/totp"
)

func main() {
	// Create a new Digits object from a number. Choices are:
	//   6 and 8.
	digits := totp.NewDigits(8)

	fmt.Println("Digits:", digits)
	fmt.Println("Digits ID:", digits.OTPDigits())

	// DigitsEight is equivalent to NewDigits(8)
	if totp.DigitsEight == totp.NewDigits(8) {
		fmt.Println("Digit 8", "OK")
	}

	// DigitsSix is equivalent to NewDigits(6)
	if totp.DigitsSix == totp.NewDigits(6) {
		fmt.Println("Digit 6", "OK")
	}

}
Output:

Digits: 8
Digits ID: 8
Digit 8 OK
Digit 6 OK
const (
	// DigitsSix is the default number of digits in a TOTP passcode.
	DigitsSix Digits = 6
	// DigitsEight is an alternative number of digits in a TOTP passcode.
	DigitsEight Digits = 8
)

func NewDigits

func NewDigits(digits int) Digits

NewDigits returns a new Digits object from the given value.

func (Digits) OTPDigits

func (d Digits) OTPDigits() otp.Digits

OTPDigits returns the value in otp.Digits type. Undefined Digits will always return otp.DigitsSix.

type Key

type Key struct {
	Options Options // Options to be stored.
	Secret  Secret  // The secret key.
}

Key is a struct that holds the TOTP secret and its options.

Example
package main

import (
	"fmt"
	"log"

	"github.com/KEINOS/go-totp/totp"
)

func main() {
	// Generate a new secret key
	Issuer := "Example.com"
	AccountName := "alice@example.com"

	key, err := totp.GenerateKey(Issuer, AccountName)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Issuer:", key.Options.AccountName)
	fmt.Println("AccountName:", key.Options.AccountName)

}
Output:

Issuer: alice@example.com
AccountName: alice@example.com

func GenerateKey

func GenerateKey(issuer string, accountName string) (*Key, error)

GenerateKey creates a new Key object with default options. Which is:

SHA-512 hash for HMAC, 30 seconds of period, 64 byte size of secret and
6 digits of passcode.

To specify custom options, use GenerateKeyCustom().

func GenerateKeyCustom

func GenerateKeyCustom(options Options) (*Key, error)

GenerateKeyCustom creates a new Key object with custom options. With this function you can specify the algorithm, period, secret size and digits.

func GenerateKeyURI

func GenerateKeyURI(uri string) (*Key, error)

GenerateKeyURI creates a new Key object from an TOTP uri/url.

The URL format is documented here:

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

import (
	"fmt"
	"log"

	"github.com/KEINOS/go-totp/totp"
)

func main() {
	origin := "otpauth://totp/Example.com:alice@example.com?algorithm=SHA1&" +
		"digits=12&issuer=Example.com&period=60&secret=QF7N673VMVHYWATKICRUA7V5MUGFG3Z3"

	key, err := totp.GenerateKeyURI(origin)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("Issuer:", key.Options.Issuer)
	fmt.Println("AccountName:", key.Options.AccountName)
	fmt.Println("Algorithm:", key.Options.Algorithm)
	fmt.Println("Digits:", key.Options.Digits)
	fmt.Println("Period:", key.Options.Period)
	fmt.Println("Secret Size:", key.Options.SecretSize)
	fmt.Println("Secret:", key.Secret.String())

}
Output:

Issuer: Example.com
AccountName: alice@example.com
Algorithm: SHA1
Digits: 12
Period: 60
Secret Size: 20
Secret: QF7N673VMVHYWATKICRUA7V5MUGFG3Z3

func (*Key) PassCode

func (k *Key) PassCode() (string, error)

PassCode generates a 6 or 8 digits passcode for the current time. The output string will be eg. "123456" or "12345678".

func (*Key) Validate

func (k *Key) Validate(passcode string) (bool, error)

Validate returns true if the given passcode is valid for the current time.

type Options

type Options struct {
	// Name of the secret issuer. (eg, organization, company, domain)
	Issuer string
	// Name of the User's Account. (eg, email address)
	AccountName string
	// Algorithm to use for HMAC. (Default: SHA512)
	Algorithm Algorithm
	// Number of seconds a TOTP hash is valid for. (Default: 30 seconds)
	Period uint
	// Size in size of the generated Secret. (Default: 20 bytes)
	SecretSize uint
	// Skew is the periods before or after the current time to allow.
	// Value of 1 allows up to Period of either side of the specified time.
	// Defaults to 0 allowed skews. Values greater than 1 are likely sketchy.
	Skew uint
	// Digits to request. DigitsSix or DigitsEight. (Default: DigitsSix)
	Digits Digits
}

Options is a struct that holds the options for a TOTP key.

Example
package main

import (
	"fmt"

	"github.com/KEINOS/go-totp/totp"
)

func main() {
	//nolint:exhaustruct // allow missing fields
	options := totp.Options{
		Issuer:      "Example.com",
		AccountName: "alice@example.com",
	}

	options.SetDefault()

	// Issuer is the name of the service who issued the secret.
	fmt.Println("Issuer:", options.Issuer)
	// Name of the owner of the secret key.
	fmt.Println("AccountName:", options.AccountName)
	// Hash algorithm to generate the passcode as HMAC.
	fmt.Println("Algorithm:", options.Algorithm)
	// Length of the passcode.
	fmt.Println("Digits:", options.Digits)
	// Valid seconds of passcode issued.
	fmt.Println("Period:", options.Period)
	// Size of the secret key in bytes.
	fmt.Println("Secret Size:", options.SecretSize)
	// Skew is an acceptable range of time before and after. Value of 1 allows
	// up to Period of either side of the specified time.
	fmt.Println("Skew:", options.Skew)

}
Output:

Issuer: Example.com
AccountName: alice@example.com
Algorithm: SHA512
Digits: 6
Period: 30
Secret Size: 20
Skew: 0

func (*Options) SetDefault

func (opts *Options) SetDefault()

SetDefault sets the undefined options to its default value.

type Secret

type Secret []byte

Secret is a byte slice that represents a secret key.

Example
package main

import (
	"fmt"
	"log"

	"github.com/KEINOS/go-totp/totp"
)

func main() {
	// The below two lines are the same but with different base-encodings.
	//nolint:gosec // potentially hardcoded credentials for testing
	base32Secret := "MZXW6IDCMFZCAYTVPJ5A"
	//nolint:gosec // potentially hardcoded credentials for testing
	base62Secret := "FegjEGvm7g03GQye"

	// Instantiate a new Secret object from a base32 encoded string.
	secret32, err := totp.NewSecretBase32(base32Secret)
	if err != nil {
		log.Fatal(err)
	}

	// Instantiate a new Secret object from a base62 encoded string.
	secret62, err := totp.NewSecretBase62(base62Secret)
	if err != nil {
		log.Fatal(err)
	}

	// Once instantiated, you can use the Secret object to get the secret in
	// different base-encodings.
	fmt.Println("Get as base62 encoded string:", secret32.Base62())
	fmt.Println("Get as base32 encoded string:", secret62.Base32())

	// String() method is equivalent to Base32()
	if secret62.String() == secret62.Base32() {
		fmt.Println("String() is equivalent to Base32()")
	}

	if secret32.String() == secret62.String() {
		fmt.Println("Two secrets are the same.")
	}

}
Output:

Get as base62 encoded string: FegjEGvm7g03GQye
Get as base32 encoded string: MZXW6IDCMFZCAYTVPJ5A
String() is equivalent to Base32()
Two secrets are the same.

func NewSecretBase32

func NewSecretBase32(base32string string) (Secret, error)

NewSecretBase32 creates a new Secret object from a base32 encoded string.

func NewSecretBase62

func NewSecretBase62(base62string string) (Secret, error)

NewSecretBase62 creates a new Secret object from a base62 encoded string.

func NewSecretBytes

func NewSecretBytes(input []byte) Secret

NewSecretBytes creates a new Secret object from a byte slice.

Example
package main

import (
	"fmt"

	"github.com/KEINOS/go-totp/totp"
)

func main() {
	data := []byte("some secret")

	// Generate a new Secret object from a byte slice.
	secret := totp.NewSecretBytes(data)

	fmt.Printf("Type: %T\n", secret)
	fmt.Printf("Value: %#v\n", secret)
	fmt.Println("Secret bytes:", secret.Bytes())
	fmt.Println("Secret string:", secret.String())
	fmt.Println("Secret Base32:", secret.Base32())
	fmt.Println("Secret Base62:", secret.Base62())

}
Output:

Type: totp.Secret
Value: totp.Secret{0x73, 0x6f, 0x6d, 0x65, 0x20, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74}
Secret bytes: [115 111 109 101 32 115 101 99 114 101 116]
Secret string: ONXW2ZJAONSWG4TFOQ
Secret Base32: ONXW2ZJAONSWG4TFOQ
Secret Base62: bfF9D3ygDyVQZp2

func (Secret) Base32

func (s Secret) Base32() string

Base32 returns the secret as a base32 encoded string. Which is the standard format used by TOTP URIs.

func (Secret) Base62

func (s Secret) Base62() string

Base62 returns the secret as a base62 encoded string.

func (Secret) Bytes

func (s Secret) Bytes() []byte

Bytes returns the secret as a byte slice.

func (Secret) String

func (s Secret) String() string

String is an implementation of the Stringer interface. It is an alias for Base32().

type URI

type URI string

URI is a struct that holds the TOTP URI.

All methods are calculated each time they are called. Therefore, it is recommended to store them. Note also that the values are not validated. For example, "Digits" may return any value.

Example
package main

import (
	"fmt"
	"log"

	"github.com/KEINOS/go-totp/totp"
)

func main() {
	origin := "otpauth://totp/Example.com:alice@example.com?algorithm=SHA1&" +
		"digits=12&issuer=Example.com&period=60&secret=QF7N673VMVHYWATKICRUA7V5MUGFG3Z3"

	uri := totp.URI(origin)

	// Check if the URI is correctly formatted with the required fields.
	if err := uri.Check(); err != nil {
		log.Fatal(err)
	}

	if uri.String() == origin {
		fmt.Println("Raw URI and String is equal: OK")
	}

	fmt.Println("Scheme:", uri.Scheme())
	fmt.Println("Host:", uri.Host())
	fmt.Println("Issuer:", uri.Issuer())
	fmt.Println("Account Name:", uri.AccountName())
	fmt.Println("Algorithm:", uri.Algorithm())
	fmt.Println("Secret:", uri.Secret().String())
	fmt.Println("Period:", uri.Period())
	fmt.Println("Digits:", uri.Digits())

}
Output:

Raw URI and String is equal: OK
Scheme: otpauth
Host: totp
Issuer: Example.com
Account Name: alice@example.com
Algorithm: SHA1
Secret: QF7N673VMVHYWATKICRUA7V5MUGFG3Z3
Period: 60
Digits: 12

func NewURI

func NewURI(uri string) URI

NewURI returns a new URI object. It simply returns the casted string as a URI object. To validate if the URI is correctly formatted, use the Check() method.

func (URI) AccountName

func (u URI) AccountName() string

AccountName returns the account name from the URI.

func (URI) Algorithm

func (u URI) Algorithm() string

Algorithm returns the algorithm from the URI.

func (URI) Check

func (u URI) Check() error

Check returns true if the URI is correctly formatted and required fields are set.

func (URI) Digits

func (u URI) Digits() uint

Digits returns the number of digits a TOTP hash should have from the URI query.

func (URI) Host

func (u URI) Host() string

Host returns the host name from the URI. This should be `totp`.

func (URI) Issuer

func (u URI) Issuer() string

Issuer returns the issuer from the URI. Similar to IssuerFromPath() but returns the issuer from the query string instead of the path.

func (URI) IssuerFromPath

func (u URI) IssuerFromPath() string

IssuerFromPath returns the issuer from the URI. Similar to Issuer() but returns the issuer from the path instead of the query string.

Example
package main

import (
	"fmt"

	"github.com/KEINOS/go-totp/totp"
)

func main() {
	origin := "otpauth://totp/Example.com:alice@example.com?issuer=Wrong.com"

	uri := totp.URI(origin)

	fmt.Println(uri.IssuerFromPath())

}
Output:

Example.com

func (URI) Path

func (u URI) Path() string

Path returns the path from the URI. Which is used as a "label" for the TOTP. See:

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

func (URI) Period

func (u URI) Period() uint

Period returns the number of seconds a TOTP hash is valid for from the URI. If the period is not set or the URL is invalid, it returns 0.

func (URI) Scheme

func (u URI) Scheme() string

Scheme returns the scheme/protocol from the URI. This should be `otpauth`.

func (URI) Secret

func (u URI) Secret() Secret

Secret returns the secret key from the URI as a Secret object.

func (URI) String

func (u URI) String() string

String is an implementation of the Stringer interface. It just returns the raw URI.

Jump to

Keyboard shortcuts

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