otp/totp_test.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()
}
}