92 lines
2.3 KiB
Go
92 lines
2.3 KiB
Go
package otp
|
|
|
|
import (
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
// TOTPKeyOptions represents the settings or values to use when creating a TOTP Key
|
|
type TOTPKeyOptions struct {
|
|
KeyOptions
|
|
Period uint64
|
|
}
|
|
|
|
// NewTOTPKeyOptions returns a TOTPKeyOptions using sane default values and a secret key
|
|
func NewTOTPKeyOptions() TOTPKeyOptions {
|
|
return TOTPKeyOptions{
|
|
NewKeyOptions(),
|
|
DefaultPeriod,
|
|
}
|
|
}
|
|
|
|
// TOTPKey represents the TOTP family of OTP as defined in RFC6238
|
|
type TOTPKey struct {
|
|
baseKey
|
|
|
|
period uint64
|
|
}
|
|
|
|
// NewTOTPKey creates and returns a new TOTP key
|
|
// If no KeyOptions are provided, sane defaults and a random secret will be used
|
|
func NewTOTPKey(o ...TOTPKeyOptions) Key {
|
|
var opts TOTPKeyOptions
|
|
if len(o) == 0 {
|
|
opts = NewTOTPKeyOptions()
|
|
} else {
|
|
opts = o[0]
|
|
}
|
|
|
|
t := &TOTPKey{}
|
|
t.baseKey = baseKey{}
|
|
t.opts = opts.KeyOptions
|
|
t.keyType = TOTP
|
|
|
|
t.period = opts.Period
|
|
|
|
return t
|
|
}
|
|
|
|
// Period returns the time period this TOTP Key uses - default is 30 seconds
|
|
func (k *TOTPKey) Period() uint64 {
|
|
return k.period
|
|
}
|
|
|
|
func (k *TOTPKey) timeToCounter(t time.Time) uint64 {
|
|
return TimeToCounter(t, k.period)
|
|
}
|
|
|
|
// TimeToCounter converts a provided time to a counter to be used for the OTP
|
|
func TimeToCounter(t time.Time, period uint64) uint64 {
|
|
return uint64(t.Unix() / int64(period))
|
|
}
|
|
|
|
// OTP produces a one-time use code
|
|
func (k *TOTPKey) OTP() string {
|
|
i := getOTP(k.Secret(), k.timeToCounter(time.Now()), k.Length())
|
|
|
|
return k.formatCode(i)
|
|
}
|
|
|
|
// URL creates a relevant URL to distribute/share the key as detailed at https://github.com/google/google-authenticator/wiki/Key-Uri-Format
|
|
func (k *TOTPKey) URL() string {
|
|
u, vals := k.baseKey.URL()
|
|
vals.Add("period", strconv.FormatUint(k.period, 10))
|
|
u.RawQuery = vals.Encode()
|
|
return u.String()
|
|
}
|
|
|
|
// Verify compares the provided code with different potential codes within the allowed skew range. Counter should be Seconds since the Unix Epoch
|
|
func (k *TOTPKey) Verify(code string, counter ...uint64) bool {
|
|
c := k.timeToCounter(time.Now())
|
|
if len(counter) > 0 {
|
|
c = counter[0] / k.period
|
|
}
|
|
return k.verify(c, code)
|
|
}
|
|
|
|
// IntegrityCheck provides information to verify the key is the same one used in Google Authenticator - doesn't appear to be used for TOTP keys though
|
|
func (k *TOTPKey) IntegrityCheck() (string, uint64) {
|
|
i := getOTP(k.Secret(), 0, k.Length())
|
|
return k.formatCode(i), k.timeToCounter(time.Now())
|
|
}
|