/* * ChaffScheduler.h — Idle chaff injection for tgnet * Ported from telemt/tdlib-obf (MIT License, Copyright 2026 telemt community) * Adapted for tgnet: C++17, no tdlib dependencies. * * After 15s of idle traffic, emits synthetic dummy TLS records on 5s intervals * within a 4096 bytes/minute sliding budget. * Prevents idle-gap fingerprinting where absence of traffic is itself a signal. * * Disabled by default; enabled when TLS proxy connection is active. */ #ifndef CHAFF_SCHEDULER_H #define CHAFF_SCHEDULER_H #include #include #include #include "IptController.h" #include "DrsEngine.h" namespace stealth { struct ChaffPolicy { bool enabled{false}; int32_t idle_threshold_ms{15000}; // wait this long after last activity double min_interval_ms{5000.0}; // minimum gap between chaff records size_t max_bytes_per_minute{4096}; int32_t record_size_lo{50}; // chaff record size range int32_t record_size_hi{800}; }; ChaffPolicy default_chaff_policy(); class ChaffScheduler { public: ChaffScheduler(const ChaffPolicy &policy, IptController &ipt, double now_sec); // Call whenever real data is sent void note_activity(double now_sec); // Call after a chaff record is emitted void note_chaff_emitted(double now_sec, size_t bytes); // Returns true if a chaff record should be sent right now bool should_emit(double now_sec, bool has_pending_data, bool can_write) const; // Returns the monotonic time (seconds) when next wakeup is needed. // Returns 0.0 if not scheduled. double get_wakeup(double now_sec, bool has_pending_data, bool can_write) const; // Size in bytes of the next chaff record to emit int32_t next_record_size(); bool is_enabled() const noexcept { return policy_.enabled; } void set_enabled(bool v) noexcept { policy_.enabled = v; } private: struct BudgetSample { double at{0.0}; size_t bytes{0}; }; static constexpr double kBudgetWindow = 60.0; // seconds void schedule_after_activity(double now_sec); void schedule_after_chaff(double now_sec); void disarm(); void prune_budget(double now_sec); double budget_resume_at(double now_sec, size_t target_bytes) const; bool budget_allows(double now_sec, size_t target_bytes) const; double sample_interval_sec(); int32_t sample_record_size(); ChaffPolicy policy_; IptController &ipt_; std::deque budget_window_; double next_send_at_{0.0}; int32_t pending_size_{0}; }; } // namespace stealth #endif // CHAFF_SCHEDULER_H