#include "fiko/cekirdek/lisans_dosya.hpp" #include #include #include "fiko/cekirdek/kripto.hpp" #include "fiko/cekirdek/makine_kimlik.hpp" namespace fiko { namespace { std::string kirp(std::string_view s) { std::size_t a = 0, b = s.size(); while (a < b && (s[a] == ' ' || s[a] == '\t' || s[a] == '\r')) ++a; while (b > a && (s[b - 1] == ' ' || s[b - 1] == '\t' || s[b - 1] == '\r')) --b; return std::string(s.substr(a, b - a)); } // Satırlara böl: BOM atılır, her satır kırpılır, boş/yorum satırları düşer. std::vector satirlar(std::string_view metin) { if (metin.size() >= 3 && (u8)metin[0] == 0xEF && (u8)metin[1] == 0xBB && (u8)metin[2] == 0xBF) metin.remove_prefix(3); // UTF-8 BOM (Notepad/PowerShell ekler) std::vector out; std::size_t i = 0; while (i <= metin.size()) { std::size_t son = metin.find('\n', i); const bool sonSatir = (son == std::string_view::npos); if (sonSatir) son = metin.size(); std::string s = kirp(metin.substr(i, son - i)); if (!s.empty() && s[0] != '#') out.push_back(std::move(s)); i = son + 1; if (sonSatir) break; } return out; } } // namespace std::string lisansKanonikGovde(std::string_view metin) { std::string govde; for (const std::string& satir : satirlar(metin)) { const std::size_t esit = satir.find('='); if (esit == std::string::npos) continue; // bozuk satır → lisansCoz raporlar const std::string anahtar = kirp(std::string_view(satir).substr(0, esit)); if (anahtar == "imza") continue; // imza satırı gövdeye GİRMEZ govde += anahtar; govde += '='; govde += kirp(std::string_view(satir).substr(esit + 1)); govde += '\n'; } return govde; } // ── Zarf (gizlilik) katmanı ───────────────────────────────────────────────── namespace { // UYARI — BU GİZ SIR DEĞİLDİR. Dağıtılan exe'nin içindedir; tersine mühendislik // yapan biri bulur ve zarfı açar. Amaç GÜVENLİK DEĞİL GİZLİLİK: sıradan müşteri // .fkl dosyasını Notepad'de açıp komut/kota/makine listesini okuyamasın, elle // düzenlemeye kalkışmasın. Lisansın SAHTELENEMEZLİĞİ tamamen ECDSA imzasından // gelir — zarf açılsa bile imzasız/kurcalanmış lisans reddedilir. constexpr const char* ZARF_GIZ = "FikoCAD.lisans.zarf.v1/2026#akcali"; constexpr const char* ZARF_SIHIR = "FKLE1"; } // namespace void lisansZarfAnahtari(u8 out[32]) { // sha256(giz) → 32 baytlık AES anahtarı. sha256 başarısız olursa (Windows dışı) // anahtar SIFIR kalır; şifreleme/çözme zaten başarısız döner, sessiz zayıf // anahtarla iş görmez. std::string g(ZARF_GIZ); if (!kripto::sha256(g.data(), g.size(), out)) for (int i = 0; i < 32; ++i) out[i] = 0; } std::string lisansZarfla(std::string_view duzGovdeVeImza, const u8 anahtar[32]) { std::string ikili; if (!kripto::aesGcmSifrele(duzGovdeVeImza, anahtar, ikili)) return ""; std::string s(ZARF_SIHIR); s += '\n'; // 76 sütunda kır: e-posta/kopyala-yapıştır satırı bozmasın (base64Coz \n yok sayar). const std::string b64 = kripto::base64Kodla(reinterpret_cast(ikili.data()), ikili.size()); for (std::size_t i = 0; i < b64.size(); i += 76) { s += b64.substr(i, 76); s += '\n'; } return s; } std::string lisansZarfCoz(std::string_view dosyaIcerigi, const u8 anahtar[32]) { std::string_view m = dosyaIcerigi; if (m.size() >= 3 && (u8)m[0] == 0xEF && (u8)m[1] == 0xBB && (u8)m[2] == 0xBF) m.remove_prefix(3); // Notepad BOM eklemişse sihirli imza kaçmasın const std::string_view sihir(ZARF_SIHIR); if (m.size() < sihir.size() || m.substr(0, sihir.size()) != sihir) return std::string(dosyaIcerigi); // GERİYE UYUM: düz metin lisans aynen geçer const std::size_t nl = m.find('\n'); if (nl == std::string_view::npos) return ""; std::vector ikili; if (!kripto::base64Coz(m.substr(nl + 1), ikili) || ikili.empty()) return ""; std::string duz; if (!kripto::aesGcmCoz(std::string_view(reinterpret_cast(ikili.data()), ikili.size()), anahtar, duz)) return ""; return duz; } const char* kotaDonemAdi(KotaDonem d) { switch (d) { case KotaDonem::Gun: return "gün"; case KotaDonem::Hafta: return "hafta"; case KotaDonem::Ay: return "ay"; case KotaDonem::Yil: return "yıl"; default: return "toplam"; } } KotaDonem kotaDonemCoz(std::string_view s) { if (s == "gun" || s == "gün" || s == "g") return KotaDonem::Gun; if (s == "hafta" || s == "h") return KotaDonem::Hafta; if (s == "ay" || s == "a") return KotaDonem::Ay; if (s == "yil" || s == "yıl" || s == "y") return KotaDonem::Yil; return KotaDonem::Toplam; } u32 kotaPeriyotCoz(std::string_view s) { // Sayı önekini oku (yoksa 1): "5saat" → 5, "hafta" → 1. std::size_t i = 0; u64 sayi = 0; bool sayiVar = false; while (i < s.size() && s[i] >= '0' && s[i] <= '9') { sayi = sayi * 10 + (u64)(s[i] - '0'); ++i; sayiVar = true; } if (!sayiVar) sayi = 1; const std::string_view birim = s.substr(i); u64 carpan = 0; if (birim == "sn" || birim == "saniye" || birim == "s") carpan = 1; else if (birim == "dk" || birim == "dakika" || birim == "m") carpan = 60; else if (birim == "saat" || birim == "sa" || birim == "h") carpan = 3600; else if (birim == "gun" || birim == "gün" || birim == "g") carpan = 86400; else if (birim == "hafta" || birim == "hf") carpan = 604800; else if (birim == "ay" || birim == "a") carpan = 2592000; // 30 gün else if (birim == "yil" || birim == "yıl" || birim == "y") carpan = 31536000; // 365 gün else return 0; // tanınmayan/boş → ömür boyu const u64 top = sayi * carpan; return top > 0xFFFFFFFFull ? 0xFFFFFFFFu : (u32)top; } std::string kotaPeriyotMetni(u32 saniye) { if (saniye == 0) return "ömür boyu"; struct Birim { u32 sn; const char* tek; }; static const Birim birimler[] = {{31536000, "yıl"}, {2592000, "ay"}, {604800, "hafta"}, {86400, "gün"}, {3600, "saat"}, {60, "dk"}, {1, "sn"}}; for (const Birim& b : birimler) { if (saniye % b.sn == 0) { const u32 n = saniye / b.sn; return n == 1 ? std::string(b.tek) : std::to_string(n) + " " + b.tek; } } return std::to_string(saniye) + " sn"; } namespace { // "cam.gkod:100/5saat" → kimlik=cam.gkod, limit=100, periyot=18000 sn. bool kotaCoz(const std::string& deger, LisansKota& out) { const std::size_t iki = deger.find(':'); if (iki == std::string::npos) return false; out.kimlik = deger.substr(0, iki); std::string sayiKismi = deger.substr(iki + 1); const std::size_t egik = sayiKismi.find('/'); if (egik != std::string::npos) { out.periyot = kotaPeriyotCoz(sayiKismi.substr(egik + 1)); sayiKismi = sayiKismi.substr(0, egik); } else { out.periyot = 0; // ömür boyu toplam } out.limit = std::strtoull(sayiKismi.c_str(), nullptr, 10); return !out.kimlik.empty(); } } // namespace const char* lisansDurumMetni(LisansDurum d) { switch (d) { case LisansDurum::Gecerli: return "geçerli"; case LisansDurum::Bos: return "lisans dosyası yok"; case LisansDurum::Bicim: return "lisans biçimi bozuk"; case LisansDurum::ImzaYok: return "lisans imzasız"; case LisansDurum::ImzaGecersiz: return "lisans imzası geçersiz (kurcalanmış)"; case LisansDurum::UrunFarkli: return "lisans başka ürüne ait"; case LisansDurum::SuresiDolmus: return "lisans süresi dolmuş"; case LisansDurum::MakineUyusmaz: return "lisans bu makineye ait değil"; case LisansDurum::YapiUyusmaz: return "uygulama dosyası değiştirilmiş (yapı uyuşmuyor)"; case LisansDurum::UzaktanEngellendi: return "lisans satıcı tarafından uzaktan engellendi"; case LisansDurum::BaglantiKoptu: return "canlı lisans sunucusuna bağlı değil (zorunlu bağlantı ihlali)"; } return "bilinmeyen"; } LisansDurum lisansCoz(std::string_view metin, std::string_view acikAnahtarHex, LisansBilgi& out, std::string& hata) { out = LisansBilgi{}; hata.clear(); if (metin.empty()) { hata = "boş lisans"; return LisansDurum::Bos; } const std::string govde = lisansKanonikGovde(metin); std::string imza; // "paket=" satırından sonra gelenler ona ait. İŞARETÇİ DEĞİL İNDEKS: push_back // vector'ü yeniden tahsis edip işaretçiyi dangling bırakabilir. std::size_t aktifPaket = SIZE_MAX; for (const std::string& satir : satirlar(metin)) { const std::size_t esit = satir.find('='); if (esit == std::string::npos) { hata = "bozuk satır: " + satir; return LisansDurum::Bicim; } const std::string anahtar = kirp(std::string_view(satir).substr(0, esit)); const std::string deger = kirp(std::string_view(satir).substr(esit + 1)); if (anahtar == "imza") { imza = deger; // imza satırı GÖVDEYE girmez (lisansKanonikGovde dışlar) } else { if (anahtar == "musteri") out.musteri = deger; else if (anahtar == "seri") out.seri = deger; else if (anahtar == "urun") out.urun = deger; else if (anahtar == "seviye") out.seviye = deger; else if (anahtar == "grup") out.gruplar.push_back(deger); else if (anahtar == "feature") out.featureler.push_back(deger); else if (anahtar == "workbench") out.workbenchler.push_back(deger); else if (anahtar == "paket") { // Yeni paket bloğu açar: sonraki komut/kota/bitis satırları BU pakete yazılır. out.paketler.push_back(LisansPaket{deger, deger, 0, {}, {}}); aktifPaket = out.paketler.size() - 1; } else if (anahtar == "paket.ad" && aktifPaket != SIZE_MAX) out.paketler[aktifPaket].ad = deger; else if (anahtar == "paket.bitis" && aktifPaket != SIZE_MAX) out.paketler[aktifPaket].bitis = std::strtoll(deger.c_str(), nullptr, 10); else if (anahtar == "komut") { if (aktifPaket != SIZE_MAX) out.paketler[aktifPaket].komutlar.push_back(deger); else out.komutlar.push_back(deger); } else if (anahtar == "makine") out.makineHashleri.push_back(deger); else if (anahtar == "yapi") out.yapiHash = deger; else if (anahtar == "makineTolerans") out.makineTolerans = std::atoi(deger.c_str()); else if (anahtar == "bitis") out.bitis = std::strtoll(deger.c_str(), nullptr, 10); else if (anahtar == "kisitli") out.kisitli = (deger != "0"); else if (anahtar == "kota") { LisansKota k; if (!kotaCoz(deger, k)) { hata = "bozuk kota: " + deger + " (beklenen: kimlik:sayı[/gun|hafta|ay|yil])"; return LisansDurum::Bicim; } if (aktifPaket != SIZE_MAX) out.paketler[aktifPaket].kotalar.push_back(k); else out.kotalar.push_back(k); } } } if (out.seri.empty() || out.urun.empty()) { hata = "zorunlu alan eksik (seri/urun)"; return LisansDurum::Bicim; } if (imza.empty()) { hata = "imza satırı yok"; return LisansDurum::ImzaYok; } if (!kripto::imzaDogrula(govde, imza, acikAnahtarHex)) { hata = "imza doğrulanamadı"; return LisansDurum::ImzaGecersiz; } if (out.makineTolerans < 1) out.makineTolerans = 1; return LisansDurum::Gecerli; } LisansDurum lisansUygula(const LisansBilgi& l, LisansSistemi& sis, const std::vector& yerelMakineHashleri, i64 suan, bool birlestir) { // Kip: birleştirmede GENİŞ kazanır (bir lisans serbest diyorsa serbest). if (!birlestir || !l.kisitli) sis.kipAyarla(l.kisitli ? LisansSistemi::Kip::Kisitli : LisansSistemi::Kip::Serbest); for (const auto& g : l.gruplar) sis.grupIzinVer(g); for (const auto& f : l.featureler) sis.featureIzinVer(f); for (const auto& w : l.workbenchler) sis.workbenchIzinVer(w); // Paketsiz (doğrudan) komut hakları — lisans geneli bitişe tabidir. sis.komutIzinVer("arac.komut_denetim", "", 0); // Kilitlenmeyi önlemek için daima izin ver for (const auto& k : l.komutlar) sis.komutIzinVer(k, "", l.bitis); for (const auto& k : l.kotalar) sis.kotaAyarla(k.kimlik, k.limit, k.periyot); // Paketler: her paket KENDİ bitişini ve kotalarını taşır. Paket bitişi 0 ise lisans // geneli bitiş uygulanır (paket, lisanstan uzun yaşayamaz). for (const LisansPaket& p : l.paketler) { const i64 pb = p.bitis != 0 ? (l.bitis != 0 ? std::min(p.bitis, l.bitis) : p.bitis) : l.bitis; for (const auto& k : p.komutlar) sis.komutIzinVer(k, p.ad.empty() ? p.kimlik : p.ad, pb); for (const auto& k : p.kotalar) sis.kotaAyarla(k.kimlik, k.limit, k.periyot, p.ad.empty() ? p.kimlik : p.ad); } // Global bitiş: birleştirmede en GENİŞ (0 = süresiz her şeyi yener; yoksa max). if (!birlestir) { sis.gecerlilikAyarla(l.bitis); } else if (sis.gecerlilik() != 0) { sis.gecerlilikAyarla(l.bitis == 0 ? 0 : std::max(sis.gecerlilik(), l.bitis)); } LisansDurum d = LisansDurum::Gecerli; // Node-lock: toleranslı eşleşme → LisansSistemi'nin tek-hash gate'ine indirgenir. if (!l.makineHashleri.empty() && !yerelMakineHashleri.empty()) { int eslesen = 0; for (const auto& y : yerelMakineHashleri) { for (const auto& m : l.makineHashleri) { if (!y.empty() && y == m) { ++eslesen; break; } } } const bool gecti = eslesen >= l.makineTolerans; sis.makineHashAyarla("kilit", gecti ? "kilit" : "uyusmaz"); if (!gecti) d = LisansDurum::MakineUyusmaz; } // Yapı kilidi: lisans belirli bir exe'ye kesilmişse yamalı ikili reddedilir. // (Boş `yapi=` → sürümden bağımsız lisans; güncelleme sonrası çalışmaya devam eder.) if (d == LisansDurum::Gecerli && !l.yapiHash.empty()) { const std::string& yerel = uygulamaYapiHash(); if (yerel.empty() || yerel != l.yapiHash) { sis.makineHashAyarla("kilit", "yapi-uyusmaz"); // gate'i kapat (NodeLock reddi) d = LisansDurum::YapiUyusmaz; } } if (d == LisansDurum::Gecerli && l.bitis != 0 && suan > l.bitis) d = LisansDurum::SuresiDolmus; // Politikayı mühürle: bundan sonra bellekten politika kümesini genişleten bir hile // (izinliKomutlar_'a "*" enjekte etmek gibi) mührü bozar → gate Kısıtlı'ya düşer. sis.muhurle(); return d; } std::string lisansKatalogCsv(const MenuKaydi& menuler) { auto guvenli = [](const std::string& s) { std::string o = s; for (char& c : o) if (c == ';' || c == '\n' || c == '\r') c = ' '; return o; }; std::string csv = "kimlik;grup;ad;workbench;altGrup;fiyat\n"; for (const MenuOge& o : menuler.tumKayit()) { // katalog: kilitliler DAHİL csv += guvenli(komutKimligi(o)) + ';' + guvenli(o.grup) + ';' + guvenli(o.ad) + ';' + guvenli(o.workbench) + ';' + guvenli(o.altGrup) + ";\n"; } return csv; } } // namespace fiko