#include "fiko/cekirdek/lisans_uzak.hpp" #include #include "fiko/cekirdek/kripto.hpp" #include "fiko/cekirdek/lisans_dosya.hpp" // lisansKanonikGovde #include "fiko_test.hpp" using namespace fiko; namespace { // Karakter sabitleri (kaçışsız: araç katmanı kaynak içindeki kaçış dizilerini // bozabildiği için ASCII kodlarıyla kurulur). constexpr char NL = static_cast(10); constexpr char CR = static_cast(13); constexpr char TIRNAK = static_cast(34); std::string satir(const std::string& s) { return s + NL; } // JSON alan üreticileri ("anahtar":değer) — kaçışsız kurulum. std::string jD(const char* anahtar, const std::string& deger) { std::string s; s += TIRNAK; s += anahtar; s += TIRNAK; s += ':'; s += TIRNAK; s += deger; s += TIRNAK; return s; } std::string jB(const char* anahtar, bool deger) { std::string s; s += TIRNAK; s += anahtar; s += TIRNAK; s += ':'; s += deger ? "true" : "false"; return s; } std::string jN(const char* anahtar, long long deger) { std::string s; s += TIRNAK; s += anahtar; s += TIRNAK; s += ':'; s += std::to_string(deger); return s; } // Gövdeyi test anahtarıyla imzala → tam politika metni (imza satırı sonda). struct TestPolitika { std::string metin, acikHex; }; TestPolitika uret(const std::string& govde) { TestPolitika t; std::string ozelHex; if (!kripto::p256AnahtarUret(ozelHex, t.acikHex)) return t; std::string imza; if (!kripto::imzala(lisansKanonikGovde(govde), ozelHex, imza)) return t; t.metin = govde + "imza=" + imza + NL; return t; } std::string govdeEngelli() { return satir("# FikoCAD Uzak Politika v1") + satir("surum=3") + satir("zaman=1756100000") + satir("mod=engelli") + satir("engelle=FKC-2026-0001") + satir("engelle=FKC-2026-0007") + satir("mesaj=Odeme gecikmesi"); } } // namespace FIKO_TEST(uzak_politika_imza_dogrulama) { const TestPolitika t = uret(govdeEngelli()); FIKO_KONTROL(!t.metin.empty()); UzakPolitika p; std::string hata; FIKO_KONTROL(uzakPolitikaCoz(t.metin, t.acikHex, p, hata) == UzakPolitikaDurum::Gecerli); FIKO_KONTROL(p.surum == 3); FIKO_KONTROL(p.zaman == 1756100000); FIKO_KONTROL(p.mod == UzakMod::EngelliListesi); FIKO_KONTROL(p.engelliler.size() == 2); FIKO_KONTROL(p.mesaj == "Odeme gecikmesi"); } FIKO_TEST(uzak_politika_kurcalanmis_govde_reddedilir) { const TestPolitika t = uret(govdeEngelli()); // İçerikte tek karakter değişimi (sürüm 3→4) imzayı bozar. std::string kurcali = t.metin; const std::size_t konum = kurcali.find("surum=3"); FIKO_KONTROL(konum != std::string::npos); kurcali[konum + 6] = '4'; UzakPolitika p; std::string hata; FIKO_KONTROL(uzakPolitikaCoz(kurcali, t.acikHex, p, hata) == UzakPolitikaDurum::ImzaGecersiz); } FIKO_TEST(uzak_politika_yanlis_anahtar_reddedilir) { const TestPolitika t = uret(govdeEngelli()); // Başka bir anahtar çiftinin açık anahtarıyla doğrulama BAŞARISIZ olmalı. std::string ozel2, acik2; FIKO_KONTROL(kripto::p256AnahtarUret(ozel2, acik2)); UzakPolitika p; std::string hata; FIKO_KONTROL(uzakPolitikaCoz(t.metin, acik2, p, hata) == UzakPolitikaDurum::ImzaGecersiz); } FIKO_TEST(uzak_politika_bicim_hatasi) { UzakPolitika p; std::string hata; // surum/zaman eksik → biçim hatası. FIKO_KONTROL(uzakPolitikaCoz(satir("engelle=X"), "ab", p, hata) == UzakPolitikaDurum::Bicim); // imza yok → biçim hatası. FIKO_KONTROL(uzakPolitikaCoz(satir("surum=1") + satir("zaman=1"), "ab", p, hata) == UzakPolitikaDurum::Bicim); } FIKO_TEST(uzak_politika_kanonik_tolerans) { const TestPolitika t = uret(govdeEngelli()); // CRLF + BOM eklenmiş kopya AYNI imzayla geçmeli (lisansKanonikGovde bunları temizler). std::string cift; cift += static_cast(0xEF); cift += static_cast(0xBB); cift += static_cast(0xBF); for (char c : t.metin) { if (c == NL) cift += std::string(1, CR) + NL; else cift += c; } UzakPolitika p; std::string hata; FIKO_KONTROL(uzakPolitikaCoz(cift, t.acikHex, p, hata) == UzakPolitikaDurum::Gecerli); FIKO_KONTROL(p.surum == 3); } FIKO_TEST(uzak_politika_engelli_listesi_kararlari) { const TestPolitika t = uret(govdeEngelli()); UzakPolitika p; std::string hata; FIKO_KONTROL(uzakPolitikaCoz(t.metin, t.acikHex, p, hata) == UzakPolitikaDurum::Gecerli); // Kara liste: listedekiler ENGELLİ, diğerleri serbest. FIKO_KONTROL(uzakPolitikaKarar(&p, "FKC-2026-0001") == UzakKarar::Engelli); FIKO_KONTROL(uzakPolitikaKarar(&p, "FKC-2026-0007") == UzakKarar::Engelli); FIKO_KONTROL(uzakPolitikaKarar(&p, "FKC-2026-0099") == UzakKarar::Izinli); // Politika yoksa / seri boşsa Belirsiz (fail-open). FIKO_KONTROL(uzakPolitikaKarar(nullptr, "FKC-2026-0001") == UzakKarar::Belirsiz); FIKO_KONTROL(uzakPolitikaKarar(&p, "") == UzakKarar::Belirsiz); } FIKO_TEST(uzak_politika_izin_listesi_kararlari) { const std::string govde = satir("# FikoCAD Uzak Politika v1") + satir("surum=1") + satir("zaman=1756100000") + satir("mod=izin") + satir("izin=FKC-2026-0010"); const TestPolitika t = uret(govde); UzakPolitika p; std::string hata; FIKO_KONTROL(uzakPolitikaCoz(t.metin, t.acikHex, p, hata) == UzakPolitikaDurum::Gecerli); FIKO_KONTROL(p.mod == UzakMod::IzinListesi); // Beyaz liste: YALNIZ listedekiler geçer. FIKO_KONTROL(uzakPolitikaKarar(&p, "FKC-2026-0010") == UzakKarar::Izinli); FIKO_KONTROL(uzakPolitikaKarar(&p, "FKC-2026-9999") == UzakKarar::Engelli); } // ── Canlı kanal (WebSocket) karar mesajı ───────────────────────────────────── // Kararı imzala → JSON'a sar (sunucu davranışının birebir ikizi). struct TestCanli { CanliKarar karar; std::string json, acikHex; }; TestCanli canliUret(bool engelli, const char* mesaj) { TestCanli t; // CanliKarar hedef=Seri iken kimlik alanı lisans serisini taşır; JSON anahtarı "seri". t.karar.hedef = CanliHedef::Seri; t.karar.kimlik = "FKC-2026-0001"; t.karar.engelli = engelli; t.karar.mesaj = mesaj; t.karar.zaman = 1756100000; std::string ozelHex; if (!kripto::p256AnahtarUret(ozelHex, t.acikHex)) return t; std::string imza; if (!kripto::imzala(canliKararKanonik(t.karar), ozelHex, imza)) return t; std::string j = "{"; j += jD("tip", "karar"); j += ','; j += jD("seri", t.karar.kimlik); j += ','; j += jB("engelli", engelli); if (*mesaj) { j += ','; j += jD("mesaj", mesaj); } j += ','; j += jN("zaman", t.karar.zaman); j += ','; j += jD("imza", imza); j += '}'; t.json = std::move(j); return t; } FIKO_TEST(canli_karar_imza_dogrulama) { const TestCanli t = canliUret(true, "Odeme gecikmesi"); FIKO_KONTROL(!t.json.empty()); CanliKarar k; std::string hata; FIKO_KONTROL(canliKararCoz(t.json, t.acikHex, k, hata) == UzakPolitikaDurum::Gecerli); FIKO_KONTROL(k.kimlik == "FKC-2026-0001"); FIKO_KONTROL(k.engelli); FIKO_KONTROL(k.mesaj == "Odeme gecikmesi"); FIKO_KONTROL(k.zaman == 1756100000); } FIKO_TEST(canli_karar_kurcalanmis_reddedilir) { const TestCanli t = canliUret(true, ""); // İmzadan SONRA seri değiştirildi → imza artık uyuşmaz (sahte sunucu senaryosu). std::string kurcali = t.json; const std::size_t konum = kurcali.find("FKC-2026-0001"); FIKO_KONTROL(konum != std::string::npos); kurcali.replace(konum, 13, "FKC-2026-9999"); CanliKarar k; std::string hata; FIKO_KONTROL(canliKararCoz(kurcali, t.acikHex, k, hata) == UzakPolitikaDurum::ImzaGecersiz); } FIKO_TEST(canli_karar_yanlis_tip_ve_eksik_alan) { CanliKarar k; std::string hata; // tip=ping (karar değil) → biçim hatası. FIKO_KONTROL(canliKararCoz(std::string("{") + jD("tip", "ping") + "}", "ab", k, hata) == UzakPolitikaDurum::Bicim); // İMZASIZ karar (sahte sunucu) → biçim hatası. std::string imzasiz = "{"; imzasiz += jD("tip", "karar"); imzasiz += ','; imzasiz += jD("seri", "FKC-2026-0001"); imzasiz += ','; imzasiz += jB("engelli", false); imzasiz += ','; imzasiz += jN("zaman", 1756100000); imzasiz += '}'; FIKO_KONTROL(canliKararCoz(imzasiz, "ab", k, hata) == UzakPolitikaDurum::Bicim); }