Repository navigation
Expand file tree
/
Copy pathdigest_ledger.hpp
More file actions
114 lines (91 loc) · 4.16 KB
/
Copy pathdigest_ledger.hpp
File metadata and controls
114 lines (91 loc) · 4.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/*
This file is part of libhttpserver
Copyright (C) 2011-2026 Sebastiano Merlino
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see the file LICENSE in
the distribution; if not, write to the Free Software Foundation,
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
// The bounded nonce/nc replay ledger (TASK-115, plan section 3; the
// v2 nonce_nc_size analogue). One map from nonce to {expiry, last_nc},
// mutex-guarded; the ONLY mutable surface is admit(), so every read
// of an existing nonce is a read-modify-write that never evicts.
//
// The nc rule (RFC 7616 section 3.4.2 made strict): a presented nc
// must be strictly greater than the last admitted one for that nonce
// -- gaps are allowed, reuse is a replay. A slot's counter advances
// at ADMISSION, before password verification: a failed guess burns
// its nc, so an eavesdropper cannot retry the same (nonce, nc) pair
// while the password check runs. qop-absent (RFC 2617 legacy)
// requests ride the same rule with an implicit nc of 1.
//
// Bound: when a NEW nonce arrives at capacity, expired slots are
// erased first, then the smallest-expiry survivor. Eviction can only
// drop replay protection for nonces the TTL has already retired (or
// the soonest-to-retire one) -- never a live slot out of turn.
//
// max_nc (0 = unbounded, the k_no_nc_limit sentinel) is the use-count
// ceiling per nonce: once last_nc has reached it, every further use
// is exhausted (the policy's stale_nonce: re-challenge with a fresh
// nonce, stale=TRUE).
#if !defined(HTTPSERVER_COMPILATION)
#error "digest_ledger.hpp is internal; only reachable when compiling libhttpserver."
#endif
#ifndef SRC_HTTPSERVER_DETAIL_DIGEST_LEDGER_HPP_
#define SRC_HTTPSERVER_DETAIL_DIGEST_LEDGER_HPP_
#include <cstddef>
#include <cstdint>
#include <mutex>
#include <string>
#include <unordered_map>
namespace httpserver {
namespace detail {
namespace digest {
enum class nc_admission : std::uint8_t {
admitted, // strictly-increasing nc; slot advanced
replayed, // nc not greater than the last admitted one
exhausted, // last_nc reached max_nc: nonce used up
};
class digest_ledger {
public:
// max_nc value meaning "no use-count ceiling".
static constexpr std::uint32_t k_no_nc_limit = 0;
// capacity must be at least 1 (the policy factory refuses zero;
// the class treats a 0 capacity as untracked insertions rather
// than undefined behavior).
explicit digest_ledger(std::size_t capacity)
: capacity_(capacity) {}
digest_ledger(const digest_ledger&) = delete;
digest_ledger& operator=(const digest_ledger&) = delete;
// Admits one (nonce, nc) observation taken at @p now_unix. A new
// nonce is inserted with its expiry; an existing one only has its
// counter advanced. Thread-safe (one mutex, no callbacks held
// under the lock).
nc_admission admit(const std::string& nonce, std::uint64_t now_unix,
std::uint64_t expires_unix, std::uint32_t nc,
std::uint32_t max_nc);
std::size_t size() const;
private:
struct slot {
std::uint64_t expires_unix = 0;
std::uint32_t last_nc = 0;
};
// Erases room for one insertion: expired slots first, then the
// smallest-expiry survivor. Caller holds the mutex.
void make_room_locked(std::uint64_t now_unix);
mutable std::mutex mutex_;
std::unordered_map<std::string, slot> slots_;
std::size_t capacity_;
};
} // namespace digest
} // namespace detail
} // namespace httpserver
#endif // SRC_HTTPSERVER_DETAIL_DIGEST_LEDGER_HPP_