package auth import "testing" func TestPasswordHashRoundtrip(t *testing.T) { hash, err := HashPassword("correct-horse-12") if err != nil { t.Fatal(err) } // PHC 串格式与参数(§7.3-1) if len(hash) < len("$argon2id$v=19$m=19456,t=2,p=1$") || hash[:len("$argon2id$v=19$m=19456,t=2,p=1$")] != "$argon2id$v=19$m=19456,t=2,p=1$" { t.Errorf("PHC 前缀/参数错误: %s", hash) } if !VerifyPassword(hash, "correct-horse-12") { t.Error("正确口令应通过校验") } if VerifyPassword(hash, "wrong-password") { t.Error("错误口令不应通过") } // 盐随机:同口令两次哈希不同 hash2, _ := HashPassword("correct-horse-12") if hash == hash2 { t.Error("同口令两次哈希应不同(随机盐)") } // 非法串安全返回 false for _, bad := range []string{"", "$argon2id$", "$bcrypt$x$y$z", "$argon2id$v=19$m=1,t=1,p=1$!!$!!"} { if VerifyPassword(bad, "x") { t.Errorf("非法串 %q 不应通过", bad) } } } // TestVerifyPasswordMalformedParams 畸形 PHC 参数安全返回 false: // t/p=0 会使 argon2.IDKey panic、m 巨大会 OOM(评审 round2 P2-8)。 func TestVerifyPasswordMalformedParams(t *testing.T) { salt := "AAAAAAAAAAAAAAAAAAAAAA" // 16 字节 base64(raw std) hash := "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" // 32 字节 bad := []string{ "$argon2id$v=19$m=19456,t=0,p=1$" + salt + "$" + hash, "$argon2id$v=19$m=19456,t=2,p=0$" + salt + "$" + hash, "$argon2id$v=19$m=999999999,t=2,p=1$" + salt + "$" + hash, "$argon2id$v=19$m=19456,t=999,p=1$" + salt + "$" + hash, "$argon2id$v=19$m=19456,t=2,p=99$" + salt + "$" + hash, "$argon2id$v=19$m=19456,t=2,p=1$" + salt + "$AAAA", // 哈希长度异常 } for _, h := range bad { if VerifyPassword(h, "x") { t.Errorf("畸形参数不应通过: %s", h) } } } func TestTokenAndStrength(t *testing.T) { tok, err := NewToken() if err != nil { t.Fatal(err) } if len(tok) != 43 { t.Errorf("256bit base64url token 应为 43 字符,实际 %d", len(tok)) } if HashToken(tok) == tok { t.Error("token 摘要不应等于明文") } if HashToken(tok) != HashToken(tok) { t.Error("摘要应确定") } if err := CheckPasswordStrength("short12"); err == nil { t.Error("弱口令应被拒绝") } if err := CheckPasswordStrength("strong-enough-12"); err != nil { t.Errorf("合格口令不应被拒绝: %v", err) } }