#include "fiko/cekirdek/lisans_uzak.hpp" #include #include "fiko/cekirdek/kripto.hpp" #include "fiko/cekirdek/lisans_dosya.hpp" // lisansKanonikGovde (aynı kanonikleştirme) namespace fiko { namespace { // Karakter sabitleri (kaçışsız): araç katmanı kaynak içindeki ters-bölü // dizilerini bozabildiği için ASCII kodlarıyla tanımlanır. constexpr char NL = static_cast(10); // satır sonu constexpr char CR = static_cast(13); constexpr char TAB = static_cast(9); constexpr char TERS = static_cast(92); // JSON kaçış işareti constexpr char TIRNAK = static_cast(34); } // namespace const char* uzakPolitikaDurumMetni(UzakPolitikaDurum d) { switch (d) { case UzakPolitikaDurum::Gecerli: return "geçerli"; case UzakPolitikaDurum::Bicim: return "politika biçimi bozuk"; case UzakPolitikaDurum::ImzaGecersiz: return "politika imzası geçersiz (kurcalanmış)"; } return "bilinmeyen"; } UzakPolitikaDurum uzakPolitikaCoz(std::string_view metin, std::string_view acikAnahtarHex, UzakPolitika& out, std::string& hata) { out = UzakPolitika{}; // İmza doğrulaması KANONİK gövde üzerinde; alan okuma HAM satırlardan — çünkü // lisansKanonikGovde imza satırını GÖVDEDEN DIŞLAR (lisansCoz ile aynı desen). const std::string kanonik = lisansKanonikGovde(metin); std::string imzaB64; bool surumVar = false, zamanVar = false; std::size_t i = 0; while (i <= metin.size()) { std::size_t son = metin.find(NL, i); if (son == std::string_view::npos) son = metin.size(); std::string satir(metin.substr(i, son - i)); i = son + 1; // Kırp (CR / çevre boşlukları) + yorum ve boş satır at. while (!satir.empty() && (satir.back() == ' ' || satir.back() == TAB || satir.back() == NL)) satir.pop_back(); std::size_t a = 0; while (a < satir.size() && (satir[a] == ' ' || satir[a] == TAB)) ++a; satir.erase(0, a); if (satir.empty() || satir[0] == '#') { if (son == metin.size()) break; continue; } const std::size_t esit = satir.find('='); if (esit == std::string::npos) continue; // bozuk satır → yok say (politika esnek) const std::string anahtar = satir.substr(0, esit); const std::string deger = satir.substr(esit + 1); if (anahtar == "surum") { out.surum = std::strtoull(deger.c_str(), nullptr, 10); surumVar = true; } else if (anahtar == "zaman") { out.zaman = std::strtoll(deger.c_str(), nullptr, 10); zamanVar = true; } else if (anahtar == "mod") { out.mod = (deger == "izin") ? UzakMod::IzinListesi : UzakMod::EngelliListesi; } else if (anahtar == "engelle") { if (!deger.empty()) out.engelliler.push_back(deger); } else if (anahtar == "izin") { if (!deger.empty()) out.izinliler.push_back(deger); } else if (anahtar == "mesaj") { out.mesaj = deger; } else if (anahtar == "imza") { imzaB64 = deger; } // Bilinmeyen anahtarlar yok sayılır → gelecekteki sürümler geriye uyumlu. if (son == metin.size()) break; } if (!surumVar || !zamanVar) { hata = "zorunlu alan eksik (surum/zaman)"; return UzakPolitikaDurum::Bicim; } if (imzaB64.empty()) { hata = "imza satırı yok"; return UzakPolitikaDurum::Bicim; } if (acikAnahtarHex.empty()) { hata = "açık anahtar boş"; return UzakPolitikaDurum::ImzaGecersiz; } if (!kripto::imzaDogrula(kanonik, imzaB64, acikAnahtarHex)) { hata = "ECDSA doğrulaması başarısız"; return UzakPolitikaDurum::ImzaGecersiz; } return UzakPolitikaDurum::Gecerli; } const char* uzakKararMetni(UzakKarar k) { switch (k) { case UzakKarar::Izinli: return "izinli"; case UzakKarar::Engelli: return "engelli"; case UzakKarar::Belirsiz: return "belirsiz (bilgi yok)"; } return "bilinmeyen"; } UzakKarar uzakPolitikaKarar(const UzakPolitika* p, const std::string& seri) { if (!p || seri.empty()) return UzakKarar::Engelli; // BELİRSİZ ARTIK ENGELLİ (Zorunlu Uzak Onay) const auto iceriyor = [](const std::vector& liste, const std::string& s) { return std::find(liste.begin(), liste.end(), s) != liste.end(); }; if (p->mod == UzakMod::EngelliListesi) return iceriyor(p->engelliler, seri) ? UzakKarar::Engelli : UzakKarar::Izinli; return iceriyor(p->izinliler, seri) ? UzakKarar::Izinli : UzakKarar::Engelli; } // ── Canlı kanal (WebSocket) karar mesajı ───────────────────────────────────── namespace { // JSON dizge alanı: "anahtar" : "değer". Kaçış çözümü dahil (n/r/t/uXXXX/çizgi). bool jsonDizgeAl(std::string_view j, const char* anahtar, std::string& out) { std::string pat; pat += TIRNAK; pat += anahtar; pat += TIRNAK; const std::size_t p = j.find(pat); if (p == std::string_view::npos) return false; std::size_t i = p + pat.size(); while (i < j.size() && (j[i] == ' ' || j[i] == TAB)) ++i; if (i >= j.size() || j[i] != ':') return false; ++i; while (i < j.size() && (j[i] == ' ' || j[i] == TAB)) ++i; if (i >= j.size() || j[i] != TIRNAK) return false; ++i; out.clear(); while (i < j.size()) { const char c = j[i]; if (c == TIRNAK) return true; // kapanış if (c == TERS) { ++i; if (i >= j.size()) return false; const char e = j[i]; if (e == 'n') { out += NL; } else if (e == 'r') { out += CR; } else if (e == 't') { out += TAB; } else if (e == 'b' || e == 'f') { // nadir — yok say } else if (e == '/') { out += '/'; } else if (e == TIRNAK) { out += TIRNAK; } else if (e == TERS) { out += TERS; } else if (e == 'u') { // uXXXX → UTF-8 (yedeklemeler yok — mesaj metni BMP içi) if (i + 4 >= j.size()) return false; unsigned cp = 0; for (int k = 1; k <= 4; ++k) { const char h = j[i + k]; cp <<= 4; if (h >= '0' && h <= '9') cp |= (unsigned)(h - '0'); else if (h >= 'a' && h <= 'f') cp |= (unsigned)(h - 'a' + 10); else if (h >= 'A' && h <= 'F') cp |= (unsigned)(h - 'A' + 10); else return false; } i += 4; if (cp < 0x80) { out += (char)cp; } else if (cp < 0x800) { out += (char)(0xC0 | (cp >> 6)); out += (char)(0x80 | (cp & 0x3F)); } else { out += (char)(0xE0 | (cp >> 12)); out += (char)(0x80 | ((cp >> 6) & 0x3F)); out += (char)(0x80 | (cp & 0x3F)); } } else { return false; } ++i; continue; } out += c; ++i; } return false; // kapanış yok } // JSON sayısal alan ("anahtar":123). bool jsonSayiAl(std::string_view j, const char* anahtar, long long& out) { std::string pat; pat += TIRNAK; pat += anahtar; pat += TIRNAK; const std::size_t p = j.find(pat); if (p == std::string_view::npos) return false; std::size_t i = p + pat.size(); while (i < j.size() && (j[i] == ' ' || j[i] == TAB)) ++i; if (i >= j.size() || j[i] != ':') return false; ++i; while (i < j.size() && (j[i] == ' ' || j[i] == TAB)) ++i; bool neg = false; if (i < j.size() && j[i] == '-') { neg = true; ++i; } if (i >= j.size() || j[i] < '0' || j[i] > '9') return false; long long v = 0; while (i < j.size() && j[i] >= '0' && j[i] <= '9') { v = v * 10 + (j[i] - '0'); ++i; } out = neg ? -v : v; return true; } // JSON boolean alan ("anahtar":true|false). bool jsonBoolAl(std::string_view j, const char* anahtar, bool& out) { std::string pat; pat += TIRNAK; pat += anahtar; pat += TIRNAK; const std::size_t p = j.find(pat); if (p == std::string_view::npos) return false; std::size_t i = p + pat.size(); while (i < j.size() && (j[i] == ' ' || j[i] == TAB)) ++i; if (i >= j.size() || j[i] != ':') return false; ++i; while (i < j.size() && (j[i] == ' ' || j[i] == TAB)) ++i; if (j.compare(i, 4, "true") == 0) { out = true; return true; } if (j.compare(i, 5, "false") == 0) { out = false; return true; } return false; } } // namespace std::string canliKararKanonik(const CanliKarar& k) { // SIRA SABİT — sunucu (Node.js) da AYNI sırayı kurmalı; değiştirme! std::string s; s += "engelle="; s += k.engelli ? "1" : "0"; s += NL; s += "seri=" + k.kimlik + NL; s += "mesaj=" + k.mesaj + NL; s += "zaman=" + std::to_string(k.zaman) + NL; return s; } UzakPolitikaDurum canliKararCoz(std::string_view json, std::string_view acikAnahtarHex, CanliKarar& out, std::string& hata) { out = CanliKarar{}; std::string tip; if (!jsonDizgeAl(json, "tip", tip) || tip != "karar") { hata = "tip=karar değil"; return UzakPolitikaDurum::Bicim; } if (!jsonDizgeAl(json, "seri", out.kimlik) || out.kimlik.empty()) { hata = "seri eksik"; return UzakPolitikaDurum::Bicim; } if (!jsonBoolAl(json, "engelli", out.engelli)) { hata = "engelli eksik"; return UzakPolitikaDurum::Bicim; } jsonDizgeAl(json, "mesaj", out.mesaj); // opsiyonel long long zaman = 0; if (!jsonSayiAl(json, "zaman", zaman)) { hata = "zaman eksik"; return UzakPolitikaDurum::Bicim; } out.zaman = zaman; std::string imzaB64; if (!jsonDizgeAl(json, "imza", imzaB64) || imzaB64.empty()) { hata = "imza eksik"; return UzakPolitikaDurum::Bicim; } if (acikAnahtarHex.empty()) { hata = "açık anahtar boş"; return UzakPolitikaDurum::ImzaGecersiz; } if (!kripto::imzaDogrula(canliKararKanonik(out), imzaB64, acikAnahtarHex)) { hata = "ECDSA doğrulaması başarısız"; return UzakPolitikaDurum::ImzaGecersiz; } return UzakPolitikaDurum::Gecerli; } } // namespace fiko