// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 package password import ( "strings" "testing" ) func TestHashAndVerifyRoundTrip(t *testing.T) { encoded, err := Hash("correct horse battery staple") if err != nil { t.Fatalf("Hash: %v", err) } if !strings.HasPrefix(encoded, "scrypt$16384$8$1$") { t.Fatalf("encoded = %q, want scrypt$16384$8$1$ prefix", encoded) } if !Verify("correct horse battery staple", encoded) { t.Fatal("Verify returned false for the correct password") } if Verify("wrong password", encoded) { t.Fatal("Verify returned true for a wrong password") } } func TestHashRejectsEmpty(t *testing.T) { if _, err := Hash(""); err != ErrEmpty { t.Fatalf("err = %v, want ErrEmpty", err) } } func TestHashRejectsTooLong(t *testing.T) { if _, err := Hash(strings.Repeat("x", MaxPasswordLength+1)); err == nil { t.Fatal("want error for over-long password") } } func TestVerifyRejectsMalformed(t *testing.T) { for _, encoded := range []string{ "", "not-a-hash", "bcrypt$16384$8$1$c2FsdA==$aGFzaA==", "scrypt$16384$8$c2FsdA==$aGFzaA==", "scrypt$abc$8$1$c2FsdA==$aGFzaA==", "scrypt$16384$8$1$!!!notb64==$aGFzaA==", } { if Verify("secret", encoded) { t.Fatalf("Verify(%q) = true, want false", encoded) } } } func TestVerifyRejectsWeakParameters(t *testing.T) { // N=1024, r=8, p=1 with a well-formed 32-byte hash: below the floor. encoded := "scrypt$1024$8$1$c2FsdHNhbHRzYWx0c2E=$aGFzaGhhc2hoYXNoaGFzaGhhc2hoYXNoaGFzaA==" if Verify("secret", encoded) { t.Fatal("Verify accepted weak scrypt parameters") } if !NeedsRehash(encoded) { t.Fatal("NeedsRehash = false for weak parameters") } } func TestVerifyRejectsExcessiveMemory(t *testing.T) { // N and r parse and clear the floor, but 128*N*r exceeds the bound. encoded := "scrypt$1048576$1024$1$c2FsdHNhbHRzYWx0c2E=$aGFzaGhhc2hoYXNoaGFzaGhhc2hoYXNoaGFzaA==" if Verify("secret", encoded) { t.Fatal("Verify accepted an excessive-memory hash") } } func TestNeedsRehash(t *testing.T) { encoded, err := Hash("password123") if err != nil { t.Fatalf("Hash: %v", err) } if NeedsRehash(encoded) { t.Fatal("NeedsRehash = true for a current-policy hash") } if !NeedsRehash("garbage") { t.Fatal("NeedsRehash = false for garbage") } }