otp/hotp.go

88 lines
2.0 KiB
Go

package otp
import "strconv"
// HOTPKeyOptions represents the settings or values to use when creating a HOTP Key
type HOTPKeyOptions struct {
KeyOptions
Counter uint64
}
// NewHOTPKeyOptions returns a HOTPKeyOptions using sane default values and a secret key
func NewHOTPKeyOptions() HOTPKeyOptions {
return HOTPKeyOptions{
NewKeyOptions(),
DefaultCounter,
}
}
// HOTPKey represents the HOTP family of OTP as found in RFC4226
type HOTPKey struct {
baseKey
counter uint64
}
// NewHOTPKey creates and returns a new HOTP key
// If no KeyOptions are provided, sane defaults and a random secret will be used
func NewHOTPKey(o ...HOTPKeyOptions) Key {
var opts HOTPKeyOptions
if len(o) == 0 {
opts = NewHOTPKeyOptions()
} else {
opts = o[0]
}
h := &HOTPKey{}
h.baseKey = baseKey{}
h.opts = opts.KeyOptions
h.keyType = HOTP
h.counter = opts.Counter
return h
}
// OTP produces a OTP code
func (k *HOTPKey) OTP() string {
// need to increment counter so that code is different each time
// Google Authenticator appears to increment before generating the code
k.counter++
i := getOTP(k.Secret(), k.counter, 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 *HOTPKey) URL() string {
u, vals := k.baseKey.URL()
vals.Add("counter", strconv.FormatUint(k.counter, 10))
u.RawQuery = vals.Encode()
return u.String()
}
// Verify compares the provided code with different potential codes within the HOTPKeys allowed skew range
func (k *HOTPKey) Verify(code string, counter ...uint64) bool {
c := k.counter
if len(counter) > 0 {
c = counter[0]
}
success := k.verify(c, code)
if success {
k.counter++
}
return success
}
// IntegrityCheck provides information to verify the key is the same one used in Google Authenticator
func (k *HOTPKey) IntegrityCheck() (string, uint64) {
i := getOTP(k.Secret(), 0, k.Length())
return k.formatCode(i), k.counter
}