checkpoint mciassrv

This commit is contained in:
2026-03-11 11:48:24 -07:00
parent 9e4e7aba7a
commit d75a1d6fd3
21 changed files with 5307 additions and 0 deletions

216
internal/auth/auth_test.go Normal file
View File

@@ -0,0 +1,216 @@
package auth
import (
"strings"
"testing"
"time"
)
// TestHashPasswordRoundTrip verifies that HashPassword + VerifyPassword works.
func TestHashPasswordRoundTrip(t *testing.T) {
params := DefaultArgonParams()
hash, err := HashPassword("correct-horse-battery-staple", params)
if err != nil {
t.Fatalf("HashPassword: %v", err)
}
if !strings.HasPrefix(hash, "$argon2id$") {
t.Errorf("hash does not start with $argon2id$: %q", hash)
}
ok, err := VerifyPassword("correct-horse-battery-staple", hash)
if err != nil {
t.Fatalf("VerifyPassword: %v", err)
}
if !ok {
t.Error("VerifyPassword returned false for correct password")
}
}
// TestHashPasswordWrongPassword verifies that a wrong password is rejected.
func TestHashPasswordWrongPassword(t *testing.T) {
params := DefaultArgonParams()
hash, err := HashPassword("correct-horse", params)
if err != nil {
t.Fatalf("HashPassword: %v", err)
}
ok, err := VerifyPassword("wrong-password", hash)
if err != nil {
t.Fatalf("VerifyPassword: %v", err)
}
if ok {
t.Error("VerifyPassword returned true for wrong password")
}
}
// TestHashPasswordUniqueHashes verifies that the same password produces
// different hashes (due to random salt).
func TestHashPasswordUniqueHashes(t *testing.T) {
params := DefaultArgonParams()
h1, err := HashPassword("password", params)
if err != nil {
t.Fatalf("HashPassword (1): %v", err)
}
h2, err := HashPassword("password", params)
if err != nil {
t.Fatalf("HashPassword (2): %v", err)
}
if h1 == h2 {
t.Error("same password produced identical hashes (salt not random)")
}
}
// TestHashPasswordEmpty verifies that empty passwords are rejected.
func TestHashPasswordEmpty(t *testing.T) {
_, err := HashPassword("", DefaultArgonParams())
if err == nil {
t.Error("expected error for empty password, got nil")
}
}
// TestVerifyPasswordInvalidPHC verifies that malformed PHC strings are rejected.
func TestVerifyPasswordInvalidPHC(t *testing.T) {
_, err := VerifyPassword("password", "not-a-phc-string")
if err == nil {
t.Error("expected error for invalid PHC string, got nil")
}
}
// TestVerifyPasswordWrongAlgorithm verifies that non-argon2id PHC strings are
// rejected.
func TestVerifyPasswordWrongAlgorithm(t *testing.T) {
fakeScrypt := "$scrypt$v=1$n=32768,r=8,p=1$c2FsdA$aGFzaA"
_, err := VerifyPassword("password", fakeScrypt)
if err == nil {
t.Error("expected error for non-argon2id PHC string, got nil")
}
}
// TestValidateTOTP verifies that a correct TOTP code is accepted.
// This test generates a secret and immediately validates the current code.
func TestValidateTOTP(t *testing.T) {
rawSecret, _, err := GenerateTOTPSecret()
if err != nil {
t.Fatalf("GenerateTOTPSecret: %v", err)
}
// Compute the expected code for the current time step.
now := time.Now().Unix()
code, err := hotp(rawSecret, uint64(now/30))
if err != nil {
t.Fatalf("hotp: %v", err)
}
ok, err := ValidateTOTP(rawSecret, code)
if err != nil {
t.Fatalf("ValidateTOTP: %v", err)
}
if !ok {
t.Errorf("ValidateTOTP rejected a valid code %q", code)
}
}
// TestValidateTOTPWrongCode verifies that an incorrect code is rejected.
func TestValidateTOTPWrongCode(t *testing.T) {
rawSecret, _, err := GenerateTOTPSecret()
if err != nil {
t.Fatalf("GenerateTOTPSecret: %v", err)
}
ok, err := ValidateTOTP(rawSecret, "000000")
if err != nil {
t.Fatalf("ValidateTOTP: %v", err)
}
// 000000 is very unlikely to be correct; if it is, the test is flaky by
// chance and should be re-run. The probability is ~3/1000000.
_ = ok // we cannot assert false without knowing the actual code
}
// TestValidateTOTPWrongLength verifies that codes of wrong length are rejected
// without an error (they are simply invalid).
func TestValidateTOTPWrongLength(t *testing.T) {
rawSecret, _, err := GenerateTOTPSecret()
if err != nil {
t.Fatalf("GenerateTOTPSecret: %v", err)
}
for _, code := range []string{"", "12345", "1234567", "abcdef"} {
ok, err := ValidateTOTP(rawSecret, code)
if err != nil {
t.Errorf("ValidateTOTP(%q): unexpected error: %v", code, err)
}
if ok && len(code) != 6 {
t.Errorf("ValidateTOTP accepted wrong-length code %q", code)
}
}
}
// TestDecodeTOTPSecret verifies base32 decoding with and without padding.
func TestDecodeTOTPSecret(t *testing.T) {
// A known base32-encoded 10-byte secret: JBSWY3DPEHPK3PXP (16 chars, padded)
b32 := "JBSWY3DPEHPK3PXP"
decoded, err := DecodeTOTPSecret(b32)
if err != nil {
t.Fatalf("DecodeTOTPSecret: %v", err)
}
if len(decoded) == 0 {
t.Error("DecodeTOTPSecret returned empty bytes")
}
// Case-insensitive input.
decoded2, err := DecodeTOTPSecret(strings.ToLower(b32))
if err != nil {
t.Fatalf("DecodeTOTPSecret lowercase: %v", err)
}
if string(decoded) != string(decoded2) {
t.Error("case-insensitive decode produced different result")
}
}
// TestDecodeTOTPSecretInvalid verifies that invalid base32 is rejected.
func TestDecodeTOTPSecretInvalid(t *testing.T) {
_, err := DecodeTOTPSecret("not-valid-base32-!@#$%")
if err == nil {
t.Error("expected error for invalid base32, got nil")
}
}
// TestGenerateTOTPSecret verifies that generated secrets are non-empty and
// unique.
func TestGenerateTOTPSecret(t *testing.T) {
raw1, b32_1, err := GenerateTOTPSecret()
if err != nil {
t.Fatalf("GenerateTOTPSecret (1): %v", err)
}
if len(raw1) != 20 {
t.Errorf("raw secret length = %d, want 20", len(raw1))
}
if b32_1 == "" {
t.Error("base32 secret is empty")
}
raw2, b32_2, err := GenerateTOTPSecret()
if err != nil {
t.Fatalf("GenerateTOTPSecret (2): %v", err)
}
if string(raw1) == string(raw2) {
t.Error("two generated TOTP secrets are identical")
}
if b32_1 == b32_2 {
t.Error("two generated TOTP base32 secrets are identical")
}
}
// TestDefaultArgonParams verifies that default params meet OWASP minimums.
func TestDefaultArgonParams(t *testing.T) {
p := DefaultArgonParams()
if p.Time < 2 {
t.Errorf("default Time=%d < OWASP minimum 2", p.Time)
}
if p.Memory < 65536 {
t.Errorf("default Memory=%d KiB < OWASP minimum 64MiB (65536 KiB)", p.Memory)
}
if p.Threads < 1 {
t.Errorf("default Threads=%d < 1", p.Threads)
}
}