Network System 0.1.1
High-performance modular networking library for scalable client-server applications
Loading...
Searching...
No Matches
session_ticket_store.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2024, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
6
7#include <algorithm>
8
10{
11
12// ============================================================================
13// session_ticket_info Implementation
14// ============================================================================
15
16auto session_ticket_info::is_valid() const noexcept -> bool
17{
18 if (ticket_data.empty())
19 {
20 return false;
21 }
22
23 auto now = std::chrono::system_clock::now();
24 return expiry > now;
25}
26
27auto session_ticket_info::get_obfuscated_age() const noexcept -> uint32_t
28{
29 auto now = std::chrono::system_clock::now();
30 auto age = std::chrono::duration_cast<std::chrono::milliseconds>(
31 now - received_time);
32
33 // Obfuscate the age by adding ticket_age_add (wrapping on overflow)
34 uint32_t age_ms = static_cast<uint32_t>(age.count());
35 return age_ms + ticket_age_add;
36}
37
38// ============================================================================
39// session_ticket_store Implementation
40// ============================================================================
41
42auto session_ticket_store::store(const std::string& server,
43 unsigned short port,
44 const session_ticket_info& ticket) -> void
45{
46 std::lock_guard<std::mutex> lock(mutex_);
47 auto key = make_key(server, port);
48 tickets_[key] = ticket;
49}
50
51auto session_ticket_store::retrieve(const std::string& server,
52 unsigned short port) const
53 -> std::optional<session_ticket_info>
54{
55 std::lock_guard<std::mutex> lock(mutex_);
56 auto key = make_key(server, port);
57
58 auto it = tickets_.find(key);
59 if (it == tickets_.end())
60 {
61 return std::nullopt;
62 }
63
64 // Check if the ticket is still valid
65 if (!it->second.is_valid())
66 {
67 return std::nullopt;
68 }
69
70 return it->second;
71}
72
73auto session_ticket_store::remove(const std::string& server,
74 unsigned short port) -> bool
75{
76 std::lock_guard<std::mutex> lock(mutex_);
77 auto key = make_key(server, port);
78 return tickets_.erase(key) > 0;
79}
80
82{
83 std::lock_guard<std::mutex> lock(mutex_);
84
85 size_t removed = 0;
86 auto now = std::chrono::system_clock::now();
87
88 for (auto it = tickets_.begin(); it != tickets_.end();)
89 {
90 if (it->second.expiry <= now)
91 {
92 it = tickets_.erase(it);
93 ++removed;
94 }
95 else
96 {
97 ++it;
98 }
99 }
100
101 return removed;
102}
103
105{
106 std::lock_guard<std::mutex> lock(mutex_);
107 tickets_.clear();
108}
109
110auto session_ticket_store::size() const -> size_t
111{
112 std::lock_guard<std::mutex> lock(mutex_);
113 return tickets_.size();
114}
115
116auto session_ticket_store::has_ticket(const std::string& server,
117 unsigned short port) const -> bool
118{
119 std::lock_guard<std::mutex> lock(mutex_);
120 auto key = make_key(server, port);
121
122 auto it = tickets_.find(key);
123 if (it == tickets_.end())
124 {
125 return false;
126 }
127
128 return it->second.is_valid();
129}
130
131auto session_ticket_store::make_key(const std::string& server,
132 unsigned short port) -> std::string
133{
134 return server + ":" + std::to_string(port);
135}
136
137// ============================================================================
138// replay_filter Implementation
139// ============================================================================
140
142 : config_{}
143{
144}
145
147 : config_(cfg)
148{
149}
150
152 std::span<const uint8_t> nonce,
153 std::chrono::system_clock::time_point timestamp) -> bool
154{
155 std::lock_guard<std::mutex> lock(mutex_);
156
157 // First, cleanup old entries
158 auto window_start = timestamp - config_.window_size;
159
160 entries_.erase(
161 std::remove_if(entries_.begin(), entries_.end(),
162 [&window_start](const nonce_entry& entry) {
163 return entry.timestamp < window_start;
164 }),
165 entries_.end());
166
167 // Check if this nonce already exists
168 std::vector<uint8_t> nonce_vec(nonce.begin(), nonce.end());
169 for (const auto& entry : entries_)
170 {
171 if (entry.nonce == nonce_vec)
172 {
173 // Replay detected
174 return false;
175 }
176 }
177
178 // Enforce max entries limit
179 if (entries_.size() >= config_.max_entries)
180 {
181 // Remove oldest entries
182 std::sort(entries_.begin(), entries_.end(),
183 [](const nonce_entry& a, const nonce_entry& b) {
184 return a.timestamp < b.timestamp;
185 });
186
187 size_t to_remove = entries_.size() / 4; // Remove 25% of oldest
188 entries_.erase(entries_.begin(),
189 entries_.begin() + static_cast<ptrdiff_t>(to_remove));
190 }
191
192 // Record the new nonce
193 entries_.push_back({std::move(nonce_vec), timestamp});
194
195 return true;
196}
197
198auto replay_filter::cleanup(std::chrono::system_clock::time_point now) -> size_t
199{
200 std::lock_guard<std::mutex> lock(mutex_);
201
202 auto window_start = now - config_.window_size;
203 size_t original_size = entries_.size();
204
205 entries_.erase(
206 std::remove_if(entries_.begin(), entries_.end(),
207 [&window_start](const nonce_entry& entry) {
208 return entry.timestamp < window_start;
209 }),
210 entries_.end());
211
212 return original_size - entries_.size();
213}
214
216{
217 std::lock_guard<std::mutex> lock(mutex_);
218 entries_.clear();
219}
220
221auto replay_filter::size() const -> size_t
222{
223 std::lock_guard<std::mutex> lock(mutex_);
224 return entries_.size();
225}
226
227} // namespace kcenon::network::protocols::quic
auto check_and_record(std::span< const uint8_t > nonce, std::chrono::system_clock::time_point timestamp=std::chrono::system_clock::now()) -> bool
Check if data should be accepted (not a replay)
auto cleanup(std::chrono::system_clock::time_point now=std::chrono::system_clock::now()) -> size_t
Remove old entries outside the window.
replay_filter()
Construct a replay filter with default configuration.
auto size() const -> size_t
Get the number of tracked nonces.
auto clear() -> void
Clear all recorded nonces.
auto cleanup_expired() -> size_t
Remove all expired tickets from the store.
auto remove(const std::string &server, unsigned short port) -> bool
Remove a session ticket for a server.
auto size() const -> size_t
Get the number of stored tickets.
auto has_ticket(const std::string &server, unsigned short port) const -> bool
Check if a valid ticket exists for a server.
static auto make_key(const std::string &server, unsigned short port) -> std::string
Generate a key for the ticket map.
std::unordered_map< std::string, session_ticket_info > tickets_
Ticket storage (key: "server:port")
auto retrieve(const std::string &server, unsigned short port) const -> std::optional< session_ticket_info >
Retrieve a session ticket for a server.
auto store(const std::string &server, unsigned short port, const session_ticket_info &ticket) -> void
Store a session ticket for a server.
std::vector< uint8_t > nonce
Contains session ticket data for 0-RTT resumption.
std::chrono::system_clock::time_point received_time
Time when the ticket was received.
std::chrono::system_clock::time_point expiry
Ticket expiration time.
auto is_valid() const noexcept -> bool
Check if the ticket is still valid (not expired)
auto get_obfuscated_age() const noexcept -> uint32_t
Get obfuscated ticket age (RFC 8446 Section 4.2.11.1)
uint32_t ticket_age_add
Ticket age add value for obfuscation (RFC 8446)
std::vector< uint8_t > ticket_data
Raw session ticket data from TLS 1.3 NewSessionTicket.