83 lines
2.3 KiB
Go
83 lines
2.3 KiB
Go
// Package otp: original code from the now-removed github.com/hgfischer/go-otp repository
|
|
package otp
|
|
|
|
import "time"
|
|
|
|
// TOTP is used to generate tokens based on RFC-6238.
|
|
//
|
|
// Example:
|
|
//
|
|
// totp := &TOTP{Secret: "your-secret", IsBase32Secret: true}
|
|
// token := totp.Get()
|
|
//
|
|
// TOTP assumes a set of default values for Secret, Length, Time, Period, WindowBack, WindowForward and IsBase32Secret
|
|
//
|
|
// If no Secret is informed, TOTP will generate a random one that you need to store with the Counter, for future token
|
|
// verifications.
|
|
//
|
|
// Check this package constants to see the current default values.
|
|
type TOTP struct {
|
|
Secret string // The secret used to generate a token
|
|
Length uint8 // The token length
|
|
Time time.Time // The time used to generate the token
|
|
IsBase32Secret bool //
|
|
Period uint8 // The step size to slice time, in seconds
|
|
WindowBack uint8 // How many steps HOTP will go backwards to validate a token
|
|
WindowForward uint8 // How many steps HOTP will go forward to validate a token
|
|
}
|
|
|
|
func (t *TOTP) setDefaults() {
|
|
if len(t.Secret) == 0 {
|
|
t.Secret = generateRandomSecret(DefaultRandomSecretLength, t.IsBase32Secret)
|
|
}
|
|
if t.Length == 0 {
|
|
t.Length = DefaultLength
|
|
}
|
|
if t.Time.IsZero() {
|
|
t.Time = time.Now()
|
|
}
|
|
if t.Period == 0 {
|
|
t.Period = DefaultPeriod
|
|
}
|
|
if t.WindowBack == 0 {
|
|
t.WindowBack = DefaultWindowBack
|
|
}
|
|
if t.WindowForward == 0 {
|
|
t.WindowForward = DefaultWindowForward
|
|
}
|
|
}
|
|
|
|
func (t *TOTP) normalize() {
|
|
if t.Length > MaxLength {
|
|
t.Length = MaxLength
|
|
}
|
|
}
|
|
|
|
// Get a time-based token
|
|
func (t *TOTP) Get() string {
|
|
t.setDefaults()
|
|
t.normalize()
|
|
ts := uint64(t.Time.Unix() / int64(t.Period))
|
|
hotp := &HOTP{Secret: t.Secret, Counter: ts, Length: t.Length, IsBase32Secret: t.IsBase32Secret}
|
|
return hotp.Get()
|
|
}
|
|
|
|
// Now is a fluent interface to set the TOTP generator's time to the current date/time
|
|
func (t *TOTP) Now() *TOTP {
|
|
t.Time = time.Now()
|
|
return t
|
|
}
|
|
|
|
// Verify a token with the current settings, including the WindowBack and WindowForward
|
|
func (t TOTP) Verify(token string) bool {
|
|
t.setDefaults()
|
|
t.normalize()
|
|
givenTime := t.Time
|
|
for i := int(t.WindowBack) * -1; i <= int(t.WindowForward); i++ {
|
|
t.Time = givenTime.Add(time.Second * time.Duration(int(t.Period)*i))
|
|
if t.Get() == token {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|