package services import ( "bssapp-backend/models" "crypto/md5" "crypto/sha1" "encoding/hex" "log" "strings" "golang.org/x/crypto/bcrypt" ) // CheckPasswordWithLegacy // - SADECE doğrulama yapar // - DB write YAPMAZ // - Migration kararını caller verir func CheckPasswordWithLegacy(user *models.User, plain string) bool { if user == nil { return false } if plain == "" { return false } stored := strings.TrimSpace(user.Upass) if stored == "" { return false } // 1️⃣ bcrypt hash mi? if isBcryptHash(stored) { candidates := make([]string, 0, 10) seen := map[string]struct{}{} add := func(v string) { if v == "" { return } if _, ok := seen[v]; ok { return } seen[v] = struct{}{} candidates = append(candidates, v) } add(plain) trimmed := strings.TrimSpace(plain) add(trimmed) bases := append([]string(nil), candidates...) for _, base := range bases { md5Sum := md5.Sum([]byte(base)) md5Hex := hex.EncodeToString(md5Sum[:]) add(md5Hex) add(strings.ToUpper(md5Hex)) sha1Sum := sha1.Sum([]byte(base)) sha1Hex := hex.EncodeToString(sha1Sum[:]) add(sha1Hex) add(strings.ToUpper(sha1Hex)) } var lastErr error for _, candidate := range candidates { if err := bcrypt.CompareHashAndPassword([]byte(stored), []byte(candidate)); err == nil { return true } else { lastErr = err } if encoded, ok := encodeLegacySingleByte(candidate); ok { if err := bcrypt.CompareHashAndPassword([]byte(stored), encoded); err == nil { return true } else { lastErr = err } } } if lastErr != nil { log.Printf( "LEGACY BCRYPT MISMATCH stored_len=%d candidates=%d last_err=%v", len(stored), len(candidates), lastErr, ) } return false } // 2️⃣ TAM LEGACY — düz metin (eski kayıtlar) if stored == plain { return true } trimmed := strings.TrimSpace(plain) if trimmed != plain && trimmed != "" && stored == trimmed { return true } // 3️⃣ Legacy hash variants seen in old dfusr.upass data. if isHexDigest(stored, 32) { sumRaw := md5.Sum([]byte(plain)) if strings.EqualFold(stored, hex.EncodeToString(sumRaw[:])) { return true } if trimmed != plain && trimmed != "" { sumTrim := md5.Sum([]byte(trimmed)) if strings.EqualFold(stored, hex.EncodeToString(sumTrim[:])) { return true } } } if isHexDigest(stored, 40) { sumRaw := sha1.Sum([]byte(plain)) if strings.EqualFold(stored, hex.EncodeToString(sumRaw[:])) { return true } if trimmed != plain && trimmed != "" { sumTrim := sha1.Sum([]byte(trimmed)) if strings.EqualFold(stored, hex.EncodeToString(sumTrim[:])) { return true } } } return false } func isBcryptHash(s string) bool { return strings.HasPrefix(s, "$2a$") || strings.HasPrefix(s, "$2b$") || strings.HasPrefix(s, "$2y$") } func isHexDigest(s string, expectedLen int) bool { if len(s) != expectedLen { return false } for _, r := range s { if (r < '0' || r > '9') && (r < 'a' || r > 'f') && (r < 'A' || r > 'F') { return false } } return true } // encodeLegacySingleByte converts text to a Turkish-compatible single-byte // representation (similar to Windows-1254 / ISO-8859-9) for legacy bcrypt data. func encodeLegacySingleByte(s string) ([]byte, bool) { out := make([]byte, 0, len(s)) for _, r := range s { switch r { case 'Ğ': out = append(out, 0xD0) case 'ğ': out = append(out, 0xF0) case 'İ': out = append(out, 0xDD) case 'ı': out = append(out, 0xFD) case 'Ş': out = append(out, 0xDE) case 'ş': out = append(out, 0xFE) default: if r >= 0 && r <= 0xFF { out = append(out, byte(r)) } else { return nil, false } } } return out, true }