136 lines
3.3 KiB
Go
136 lines
3.3 KiB
Go
package otp
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
var rfc6238Secret = "12345678901234567890"
|
|
|
|
var rfc6238CounterCodes = map[uint64]string{
|
|
59: "94287082",
|
|
1111111109: "07081804",
|
|
1111111111: "14050471",
|
|
1234567890: "89005924",
|
|
2000000000: "69279037",
|
|
20000000000: "65353130",
|
|
}
|
|
|
|
var baseRFC6238URL = "otpauth://totp/IETF:ReferenceImplementation?digits=6&issuer=IETF&period=30&secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"
|
|
|
|
func TestRFC6238CustomCode(t *testing.T) {
|
|
secret := []byte(rfc6238Secret)
|
|
for counter, code := range rfc6238CounterCodes {
|
|
generated := CustomCode(secret, counter/DefaultPeriod, uint(len(code)))
|
|
if code != generated {
|
|
t.Logf("Invalid TOTP code for counter %d", counter)
|
|
t.Logf("\tExpected:\t%s\n", code)
|
|
t.Logf("\tActual: \t%s\n", generated)
|
|
t.FailNow()
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func TestRFC6238Verify(t *testing.T) {
|
|
o := NewTOTPKeyOptions()
|
|
o.Secret = []byte(rfc6238Secret)
|
|
o.Length = 8
|
|
k := NewTOTPKey(o)
|
|
|
|
for counter, code := range rfc6238CounterCodes {
|
|
if !k.Verify(code, counter) {
|
|
t.Logf("Invalid TOTP code for counter %d", counter)
|
|
t.Logf("\tExpected:\t%s\n", code)
|
|
t.FailNow()
|
|
}
|
|
}
|
|
|
|
tKey := k.(*TOTPKey)
|
|
code, counter := k.IntegrityCheck()
|
|
expectedCounter := uint64(time.Now().Unix()) / tKey.Period()
|
|
if expectedCounter-counter > 1 {
|
|
t.Logf("Invalid time returned for TOTP Integrity check")
|
|
t.Logf("\tExpected:\t%s\n", expectedCounter)
|
|
t.Logf("\tActual: \t%s\n", counter)
|
|
t.FailNow()
|
|
}
|
|
if !k.Verify(code, 0) {
|
|
t.Logf("Invalid initial code returned for TOTP Integrity check")
|
|
t.Logf("\tExpected:\t%s\n", code)
|
|
t.FailNow()
|
|
}
|
|
|
|
}
|
|
|
|
func TestRFC6238CustomValidate(t *testing.T) {
|
|
secret := []byte(rfc6238Secret)
|
|
for counter, code := range rfc6238CounterCodes {
|
|
|
|
if !ValidateCustom(secret, counter/DefaultPeriod, code) {
|
|
t.Logf("Invalid HOTP code for counter %d", counter)
|
|
t.Logf("\tExpected:\t%s\n", code)
|
|
t.FailNow()
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
func TestRFC6238FromURL(t *testing.T) {
|
|
|
|
k, err := FromURL(baseRFC6238URL)
|
|
if err != nil {
|
|
t.Log(err.Error())
|
|
t.FailNow()
|
|
}
|
|
if k.URL() != baseRFC6238URL {
|
|
t.Log("Invalid URL from RFC 6238 reference TOTP key")
|
|
t.Logf("\tExpected:\t%s\n", baseRFC6238URL)
|
|
t.Logf("\tActual: \t%s\n", k.URL())
|
|
t.FailNow()
|
|
}
|
|
|
|
}
|
|
|
|
func TestRFC6238ToURL(t *testing.T) {
|
|
|
|
o := NewTOTPKeyOptions()
|
|
o.Secret = []byte(rfc6238Secret)
|
|
o.Issuer = "IETF"
|
|
o.Label = "ReferenceImplementation"
|
|
k := NewTOTPKey(o)
|
|
|
|
if k.URL() != baseRFC6238URL {
|
|
t.Log("Invalid URL from RFC 6238 reference TOTP key")
|
|
t.Logf("\tExpected:\t%s\n", baseRFC6238URL)
|
|
t.Logf("\tActual: \t%s\n", k.URL())
|
|
t.FailNow()
|
|
}
|
|
|
|
}
|
|
|
|
func TestTOTPVerify(t *testing.T) {
|
|
k := NewTOTPKey()
|
|
first := k.OTP()
|
|
if !k.Verify(first) {
|
|
t.Logf("TOTP key %s failed to properly verify the first code - %s", k.URL(), first)
|
|
t.FailNow()
|
|
}
|
|
// Need to wait a full period
|
|
newTime := uint64(time.Now().Add(time.Duration(DefaultPeriod) * time.Second).Unix())
|
|
|
|
// Should still validate because of Allowed Skew
|
|
if !k.Verify(first) {
|
|
t.Logf("TOTP key %s failed to properly verify the first code - %s", k.URL(), first)
|
|
t.FailNow()
|
|
}
|
|
|
|
// wait another period
|
|
newTime = uint64(time.Now().Add(time.Duration(DefaultPeriod) * time.Second * 2).Unix())
|
|
if k.Verify(first, newTime) { //Key should have advanced to the next code
|
|
t.Logf("TOTP key %s should not still verify the first code - %s", k.URL(), first)
|
|
t.FailNow()
|
|
}
|
|
|
|
}
|