package password import ( "errors" "strings" "testing" "golang.org/x/crypto/bcrypt" ) func TestBcryptHashesAndVerifiesWithoutStoringPlaintext(t *testing.T) { manager, err := NewBcrypt(bcrypt.MinCost) if err != nil { t.Fatalf("NewBcrypt() error = %v", err) } encoded, err := manager.Hash("correct horse battery staple") if err != nil { t.Fatalf("Hash() error = %v", err) } if encoded == "correct horse battery staple" { t.Fatal("Hash() returned plaintext") } if err := manager.Verify(encoded, "correct horse battery staple"); err != nil { t.Fatalf("Verify(correct) error = %v", err) } if err := manager.Verify(encoded, "wrong"); !errors.Is( err, ErrPasswordMismatch, ) { t.Fatalf("Verify(wrong) error = %v", err) } } func TestBcryptRejectsInvalidCostAndOversizedPasswords(t *testing.T) { if _, err := NewBcrypt(bcrypt.MinCost - 1); err == nil { t.Fatal("NewBcrypt(invalid) error = nil") } manager, err := NewBcrypt(bcrypt.MinCost) if err != nil { t.Fatalf("NewBcrypt() error = %v", err) } oversized := strings.Repeat("x", 73) if _, err := manager.Hash("tiny"); !errors.Is( err, ErrInvalidPassword, ) { t.Fatalf("Hash(short) error = %v", err) } if _, err := manager.Hash("admin"); err != nil { t.Fatalf("Hash(five-byte password) error = %v", err) } if _, err := manager.Hash(oversized); !errors.Is( err, ErrInvalidPassword, ) { t.Fatalf("Hash(oversized) error = %v", err) } if _, err := manager.Hash( string([]byte{0xff, 0xfe, 0xfd}), ); !errors.Is(err, ErrInvalidPassword) { t.Fatalf("Hash(invalid UTF-8) error = %v", err) } if err := manager.Verify("$2a$04$invalid", oversized); !errors.Is( err, ErrPasswordMismatch, ) { t.Fatalf("Verify(oversized) error = %v", err) } manager.VerifyDummy("password-123") }