#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 { // Satır sonu karakteri (kaçışsız: char(10)). constexpr char NL = static_cast(10); } // 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() == '\t' || satir.back() == NL)) satir.pop_back(); std::size_t a = 0; while (a < satir.size() && (satir[a] == ' ' || satir[a] == '\t')) ++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 (lisansCoz'dan farklı: // politika esnek; imza yine korur) 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::Belirsiz; 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 { constexpr char NL2 = static_cast(10); // JSON dizge değerini çöz: ""anahtar"" sonrası ':' bekler, değeri okur. // Kaçışlar: " \\ \/ \b \f \r \t \uXXXX (u yalnız atlanır — BMP dışına çıkmayız). bool jsonDizgeAl(std::string_view j, const char* anahtar, std::string& out) { const std::string pat = std::string(""") + anahtar + """; 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] == '\t')) ++i; if (i >= j.size() || j[i] != ':') return false; ++i; while (i < j.size() && (j[i] == ' ' || j[i] == '\t')) ++i; if (i >= j.size() || j[i] != '"') return false; ++i; out.clear(); while (i < j.size()) { const char c = j[i]; if (c == '"') return true; // kapanış if (c == '\') { ++i; if (i >= j.size()) return false; switch (j[i]) { case 'n': out += NL2; break; case 'r': out += static_cast(13); break; case 't': out += '\t'; break; case 'b': case 'f': break; // nadir — yok say case '/': out += '/'; break; case '"': out += '"'; break; case '\': out += '\'; break; case 'u': { // \uXXXX: basit UTF-8 kodla (yedeklemeler yok — mesaj metni) 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)); } break; } default: 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) { const std::string pat = std::string(""") + anahtar + """; 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] == '\t')) ++i; if (i >= j.size() || j[i] != ':') return false; ++i; while (i < j.size() && (j[i] == ' ' || j[i] == '\t')) ++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) { const std::string pat = std::string(""") + anahtar + """; 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] == '\t')) ++i; if (i >= j.size() || j[i] != ':') return false; ++i; while (i < j.size() && (j[i] == ' ' || j[i] == '\t')) ++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 += NL2; s += "seri=" + k.seri + NL2; s += "mesaj=" + k.mesaj + NL2; s += "zaman=" + std::to_string(k.zaman) + NL2; 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.seri) || out.seri.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