package auth import ( "testing" "time" ) // TestRoundTrip verifies that a code generated for a given secret/time // validates successfully against that same secret/time. We deliberately // don't chase the exact RFC 6238 8-digit test vector digits here since // this implementation standardizes on 6-digit codes; self-consistency of // generate -> validate is what matters for correctness of our HOTP/TOTP // math and step handling. func TestRoundTrip(t *testing.T) { secret, err := GenerateSecret() if err != nil { t.Fatalf("GenerateSecret failed: %v", err) } // Fixed reference time so the test is deterministic (corresponds to // RFC 6238's T=59 test instant). refTime := time.Unix(59, 0).UTC() code, err := GenerateCode(secret, refTime) if err != nil { t.Fatalf("GenerateCode failed: %v", err) } if len(code) != 6 { t.Fatalf("expected 6-digit code, got %q", code) } if !Validate(secret, code, refTime) { t.Fatalf("Validate failed to accept code %q generated for the same secret/time", code) } } func TestWrongCodeRejected(t *testing.T) { secret, err := GenerateSecret() if err != nil { t.Fatalf("GenerateSecret failed: %v", err) } refTime := time.Unix(59, 0).UTC() code, err := GenerateCode(secret, refTime) if err != nil { t.Fatalf("GenerateCode failed: %v", err) } wrong := "000000" if code == wrong { wrong = "111111" } if Validate(secret, wrong, refTime) { t.Fatalf("Validate incorrectly accepted a wrong code") } } func TestClockSkewToleranceAndRejection(t *testing.T) { secret, err := GenerateSecret() if err != nil { t.Fatalf("GenerateSecret failed: %v", err) } refTime := time.Unix(1_000_000, 0).UTC() code, err := GenerateCode(secret, refTime) if err != nil { t.Fatalf("GenerateCode failed: %v", err) } // One step (30s) away should still validate (skew tolerance). oneStepLater := refTime.Add(30 * time.Second) if !Validate(secret, code, oneStepLater) { t.Fatalf("Validate should tolerate +-1 step (30s) of clock skew") } // Two steps (60s, i.e. > 1 step tolerance) away should NOT validate. // Use 120s to be unambiguous with respect to step boundaries. farLater := refTime.Add(120 * time.Second) if Validate(secret, code, farLater) { t.Fatalf("Validate should reject a code more than 1 step (30s) away") } }