package config import ( "encoding/hex" "os" "path/filepath" "strings" "testing" "git.wxccs.org/iceking2nd/winauth-go/internal/crypto" ) // TestParseLegacyEncryptionFlags covers the encrypted-attribute parsing // for the three letters WinAuth actually wrote, in every legal order // plus the empty / unknown-letter cases. func TestParseLegacyEncryptionFlags(t *testing.T) { cases := map[string]LegacyPasswordType{ "": LegacyPasswordNone, "y": LegacyPasswordExplicit, "u": LegacyPasswordUser, "m": LegacyPasswordMachine, "yum": LegacyPasswordExplicit | LegacyPasswordUser | LegacyPasswordMachine, "YMU": LegacyPasswordExplicit | LegacyPasswordUser | LegacyPasswordMachine, " yu ": LegacyPasswordExplicit | LegacyPasswordUser, "abc": LegacyPasswordNone, } for in, want := range cases { if got := parseLegacyEncryptionFlags(in); got != want { t.Errorf("parseLegacyEncryptionFlags(%q) = %v, want %v", in, got, want) } } } // TestDecryptLegacyExplicitRoundTrip encrypts a known plaintext using // the helpers and round-trips it through decryptLegacySecretData to // catch any salt-layout / PBKDF2 / Blowfish drift. func TestDecryptLegacyExplicitRoundTrip(t *testing.T) { plaintext := "ABCDEF1234\t6\tSHA1\t30" password := []byte("hunter2") salt := []byte{1, 2, 3, 4, 5, 6, 7, 8} key := crypto.DerivePBKDF2SHA1(password, salt) body, err := crypto.LegacyEncryptBlowfish([]byte(plaintext), key) if err != nil { t.Fatalf("encrypt: %v", err) } payload := hex.EncodeToString(salt) + body got, err := decryptLegacySecretData(payload, LegacyPasswordExplicit, password) if err != nil { t.Fatalf("decrypt: %v", err) } if got != plaintext { t.Errorf("round trip mismatch: got %q want %q", got, plaintext) } } // TestDecryptLegacyExplicitWrongPassword verifies the printable-ASCII // heuristic flags a bad passphrase rather than returning garbage to the // caller. Two random passwords almost certainly produce non-printable // Blowfish output of length 8 or more. func TestDecryptLegacyExplicitWrongPassword(t *testing.T) { plaintext := "ABCDEF1234\t6\tSHA1\t30" salt := []byte{9, 9, 9, 9, 9, 9, 9, 9} key := crypto.DerivePBKDF2SHA1([]byte("correct"), salt) body, err := crypto.LegacyEncryptBlowfish([]byte(plaintext), key) if err != nil { t.Fatalf("encrypt: %v", err) } payload := hex.EncodeToString(salt) + body _, err = decryptLegacySecretData(payload, LegacyPasswordExplicit, []byte("wrong")) if err == nil { t.Fatal("expected wrong-password rejection, got nil") } } // TestLooksLikeLegacyPlaintext covers the ASCII-printable heuristic. func TestLooksLikeLegacyPlaintext(t *testing.T) { if !looksLikeLegacyPlaintext([]byte("ABC\t123|x")) { t.Error("printable string should pass") } if looksLikeLegacyPlaintext([]byte{0x00, 0x01, 0x02}) { t.Error("control bytes should fail") } if looksLikeLegacyPlaintext(nil) { t.Error("empty buffer should fail") } // High bit / extended ASCII should also fail — WinAuth never wrote // non-ASCII into . if looksLikeLegacyPlaintext([]byte{0xff, 'A'}) { t.Error("high bit should fail") } } // TestLoadLegacyXMLPlaintext walks the full XML → Entry path with an // unencrypted Google entry and an encrypted entry the caller skipped // the password for; the encrypted one should be skipped and the // plaintext one returned. func TestLoadLegacyXMLPlaintext(t *testing.T) { const sample = ` plain ABCDEF1234 6 SHA1 30 0 ` path := writeTempXML(t, sample) cfg, err := LoadLegacyXML(path, nil) if err != nil { t.Fatalf("load: %v", err) } if len(cfg.Entries) != 1 { t.Fatalf("entries=%d, want 1", len(cfg.Entries)) } if cfg.Entries[0].Vendor != "google" || cfg.Entries[0].Name != "plain" { t.Errorf("entry mismatch: %+v", cfg.Entries[0]) } } func TestLoadLegacyXMLPasswordRequired(t *testing.T) { const sample = ` locked 0102030405060708abcdef ` path := writeTempXML(t, sample) if _, err := LoadLegacyXML(path, nil); err == nil || !strings.Contains(err.Error(), "password required") { t.Fatalf("want password-required error, got %v", err) } } func writeTempXML(t *testing.T, body string) string { t.Helper() path := filepath.Join(t.TempDir(), "winauth.xml") if err := os.WriteFile(path, []byte(body), 0o600); err != nil { t.Fatalf("write: %v", err) } return path }