Files
ihasvpn/internal/auth/auth_test.go
T
jcoffey 6c006e1d4d WireGuard server with an embedded admin console
Go backend that drives kernel WireGuard over netlink (wireguard-go as the
fallback), nftables NAT with MSS clamping, forwarding and buffer sysctls,
SQLite for peers, users, sessions, traffic history and the audit log.

React console: dashboard with live rates and usage history, peer management
with QR codes and .conf downloads, disconnect, session reset, key rotation,
expiry, client-supplied keys, settings, users with admin and viewer roles,
two-factor authentication with recovery codes, audit log.

Docker image on Alpine with compose files for bridged and host networking,
CI and GHCR publish workflows, performance notes.
2026-09-12 19:56:08 -07:00

107 lines
2.5 KiB
Go

package auth
import (
"testing"
"time"
)
func TestPasswordRoundTrip(t *testing.T) {
h, err := HashPassword("correct horse battery staple")
if err != nil {
t.Fatal(err)
}
if !VerifyPassword(h, "correct horse battery staple") {
t.Fatal("right password rejected")
}
if VerifyPassword(h, "correct horse battery stapl") {
t.Fatal("wrong password accepted")
}
if VerifyPassword("garbage", "x") {
t.Fatal("garbage hash accepted")
}
}
func TestValidatePassword(t *testing.T) {
if err := ValidatePassword("short"); err == nil {
t.Fatal("short password accepted")
}
if err := ValidatePassword("twelve chars"); err != nil {
t.Fatalf("12-character password rejected: %v", err)
}
}
func TestTOTP(t *testing.T) {
// RFC 6238 test vector: secret "12345678901234567890" (base32
// GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ), time 59 -> 287082 with SHA1.
secret := "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"
code, err := TOTPNow(secret, time.Unix(59, 0))
if err != nil {
t.Fatal(err)
}
if code != "287082" {
t.Fatalf("got %s, want 287082", code)
}
if !VerifyTOTP(secret, "287082", time.Unix(59, 0)) {
t.Fatal("valid code rejected")
}
// One step later still accepted (window of one either side).
if !VerifyTOTP(secret, "287082", time.Unix(59+30, 0)) {
t.Fatal("previous-step code rejected")
}
if VerifyTOTP(secret, "287082", time.Unix(59+120, 0)) {
t.Fatal("stale code accepted")
}
if VerifyTOTP(secret, "28708", time.Unix(59, 0)) {
t.Fatal("short code accepted")
}
}
func TestRecoveryCodes(t *testing.T) {
codes, hashes, err := NewRecoveryCodes(8)
if err != nil {
t.Fatal(err)
}
if len(codes) != 8 || len(hashes) != 8 {
t.Fatal("wrong count")
}
if HashToken(NormaliseRecoveryCode(" "+codes[0]+" ")) != hashes[0] {
t.Fatal("normalised code does not hash to stored value")
}
if len(codes[0]) != 14 {
t.Fatalf("unexpected format %q", codes[0])
}
}
func TestLimiter(t *testing.T) {
l := NewLimiter(2, time.Minute)
if ok, _ := l.Allowed("a"); !ok {
t.Fatal("fresh key blocked")
}
l.Fail("a")
l.Fail("a")
if ok, wait := l.Allowed("a"); ok || wait <= 0 {
t.Fatal("key not blocked after max failures")
}
if ok, _ := l.Allowed("b"); !ok {
t.Fatal("unrelated key blocked")
}
l.Reset("a")
if ok, _ := l.Allowed("a"); !ok {
t.Fatal("reset key still blocked")
}
}
func TestTokens(t *testing.T) {
tok, hash, err := NewToken()
if err != nil {
t.Fatal(err)
}
if HashToken(tok) != hash {
t.Fatal("hash mismatch")
}
id, err := NewID()
if err != nil || len(id) != 16 {
t.Fatalf("bad id %q %v", id, err)
}
}