88 lines
2.0 KiB
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
|
|
}
|