package otp import "testing" var rfc4226Secret = "12345678901234567890" var rfc4226Codes = []string{ "755224", "287082", "359152", "969429", "338314", "254676", "287922", "162583", "399871", "520489", } var baseRFC4226URL = "otpauth://hotp/IETF:ReferenceImplementation?counter=0&digits=6&issuer=IETF&secret=GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ" func TestRFC4226OTP(t *testing.T) { issuer := "IETF" label := "ReferenceImplementation" o := NewHOTPKeyOptions() o.Secret = []byte(rfc4226Secret) o.Issuer = issuer o.Label = label h := NewHOTPKey(o) code, counter := h.IntegrityCheck() if code != rfc4226Codes[counter] { t.Logf("Invalid HOTP code for counter %d", counter) t.Logf("\tExpected:\t%s\n", rfc4226Codes[counter]) t.Logf("\tActual: \t%s\n", code) t.FailNow() } for counter, code := range rfc4226Codes[1:] { generated := h.OTP() if code != generated { t.Logf("Invalid HOTP code for counter %d", counter) t.Logf("\tExpected:\t%s\n", code) t.Logf("\tActual: \t%s\n", generated) t.FailNow() } } if h.Issuer() != issuer { t.Log("Invalid Issuer found") t.Logf("\tExpected:\t%s\n", issuer) t.Logf("\tActual: \t%s\n", h.Issuer()) t.FailNow() } if h.Label() != label { t.Log("Invalid Issuer found") t.Logf("\tExpected:\t%s\n", label) t.Logf("\tActual: \t%s\n", h.Label()) t.FailNow() } if h.Type() != HOTP { t.Log("Invalid Key type found") t.Logf("\tExpected:\t%s\n", HOTP) t.Logf("\tActual: \t%s\n", h.Type()) t.FailNow() } } func TestRFC4226CustomCounter(t *testing.T) { issuer := "IETF" label := "ReferenceImplementation" o := NewHOTPKeyOptions() o.Secret = []byte(rfc4226Secret) o.Issuer = issuer o.Label = label h := NewHOTPKey(o) code, counter := h.IntegrityCheck() if code != rfc4226Codes[counter] { t.Logf("Invalid HOTP code for counter %d", counter) t.Logf("\tExpected:\t%s\n", rfc4226Codes[counter]) t.Logf("\tActual: \t%s\n", code) t.FailNow() } for counter, code := range rfc4226Codes[1:] { if !h.Verify(code, uint64(counter)) { t.Logf("Invalid HOTP code for counter %d", counter) t.Logf("\tExpected:\t%s\n", code) t.FailNow() } } } func TestRFC4226FromURL(t *testing.T) { h, err := FromURL(baseRFC4226URL) if err != nil { t.Log(err.Error()) t.FailNow() } k := h.(*HOTPKey) code, counter := k.IntegrityCheck() if code != rfc4226Codes[counter] { t.Logf("Invalid HOTP code for counter %d", counter) t.Logf("\tExpected:\t%s\n", rfc4226Codes[counter]) t.Logf("\tActual: \t%s\n", code) t.FailNow() } for counter, code := range rfc4226Codes[1:] { generated := h.OTP() if code != generated { t.Logf("Invalid HOTP code for counter %d", counter) t.Logf("\tExpected:\t%s\n", code) t.Logf("\tActual: \t%s\n", generated) t.FailNow() } } } func TestRFC4226ToURL(t *testing.T) { o := NewHOTPKeyOptions() o.Secret = []byte(rfc4226Secret) o.Issuer = "IETF" o.Label = "ReferenceImplementation" h := NewHOTPKey(o) if h.URL() != baseRFC4226URL { t.Log("Invalid URL from RFC 4226 reference HOTP key") t.Logf("\tExpected:\t%s\n", baseRFC4226URL) t.Logf("\tActual: \t%s\n", h.URL()) t.FailNow() } } func TestRFC4226Verify(t *testing.T) { o := NewHOTPKeyOptions() o.Secret = []byte(rfc4226Secret) h := NewHOTPKey(o) for counter, code := range rfc4226Codes { if !h.Verify(code) { t.Logf("Invalid HOTP code for counter %d", counter) t.Logf("\tExpected:\t%s\n", code) t.FailNow() } } } func TestRFC4226CustomValidate(t *testing.T) { secret := []byte(rfc4226Secret) for counter, code := range rfc4226Codes { if !ValidateCustom(secret, uint64(counter), code) { t.Logf("Invalid HOTP code for counter %d", counter) t.Logf("\tExpected:\t%s\n", code) t.FailNow() } } } func TestRFC4226CustomCode(t *testing.T) { secret := []byte(rfc4226Secret) for counter, code := range rfc4226Codes { generated := CustomCode(secret, uint64(counter), uint(len(code))) if code != generated { t.Logf("Invalid HOTP code for counter %d", counter) t.Logf("\tExpected:\t%s\n", code) t.Logf("\tActual: \t%s\n", generated) t.FailNow() } } } func TestHOTPVerify(t *testing.T) { k := NewHOTPKey() first := k.OTP() if !k.Verify(first) { //successful verify increments counter t.Logf("HOTP key %s failed to properly verify the first code - %s", k.URL(), first) t.FailNow() } if !k.Verify(first) { //successful verify increments counter t.Logf("HOTP key %s failed to properly verify the first code - %s", k.URL(), first) t.Logf("First code should still be verified because of AllowedSkew") t.FailNow() } if k.Verify(first) { //Key should have advanced to the next code t.Logf("HOTP key %s should not have verified the first code - %s", k.URL(), first) t.FailNow() } k.SetSkew(3) if !k.Verify(first) { //successful verify increments counter t.Logf("HOTP key %s failed to properly verify the first code - %s", k.URL(), first) t.Logf("First code should still be verified because of AllowedSkew") t.FailNow() } }