-
Notifications
You must be signed in to change notification settings - Fork 768
Expand file tree
/
Copy pathscram_credential_cache.h
More file actions
96 lines (80 loc) · 2.99 KB
/
Copy pathscram_credential_cache.h
File metadata and controls
96 lines (80 loc) · 2.99 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
/*
* Copyright 2026 Redpanda Data, Inc.
*
* Use of this software is governed by the Business Source License
* included in the file licenses/BSL.md
*
* As of the Change Date specified in that file, in accordance with
* the Business Source License, use of this software will be governed
* by the Apache License, Version 2.0
*/
#pragma once
#include "bytes/bytes.h"
#include "security/scram_credential.h"
#include "security/types.h"
#include "utils/chunked_kv_cache.h"
#include <array>
#include <cstddef>
#include <optional>
namespace security {
/// \brief Per-shard memoization of SCRAM password derivations.
///
/// Validating a plaintext password against a stored SCRAM credential
/// rederives the credential's stored key (RFC 5802 `Hi`, at least 4096 HMAC
/// rounds). HTTP Basic auth and SASL/PLAIN pay that cost on every
/// authentication, which is expensive enough to saturate a core at a few
/// thousand authentications per second. This cache memoizes the derivation:
/// (algorithm, password, salt, iterations) -> stored key.
///
/// Entries are keyed by an HMAC (with a random per-instance key) of the
/// inputs so plaintext passwords are not retained. The value is the stored
/// key derived from the presented password: for a correct password this is
/// the credential's stored key, already persisted in the credential store;
/// an incorrect password is cached too (so repeated bad credentials stay
/// cheap), and its derived value reveals nothing about the stored credential.
/// Either way a memory disclosure yields no credential secret beyond what the
/// store already holds. The memoized mapping is purely functional, so entries
/// never go stale: updating a credential generates a fresh salt, which
/// changes the cache key.
class scram_credential_cache {
public:
static constexpr size_t default_capacity = 1024;
struct stats_t {
size_t hits;
size_t misses;
size_t size;
};
explicit scram_credential_cache(size_t capacity);
/// Returns the memoized stored key for the derivation inputs, if any.
std::optional<bytes> get(
scram_algorithm_t mech,
const credential_password& password,
bytes_view salt,
int iterations);
/// Memoizes the stored key derived from the given inputs.
void put(
scram_algorithm_t mech,
const credential_password& password,
bytes_view salt,
int iterations,
bytes stored_key);
stats_t stats() const;
private:
static constexpr size_t digest_size = 32;
struct key_t {
std::array<char, digest_size> digest;
bool operator==(const key_t&) const = default;
template<typename H>
friend H AbslHashValue(H h, const key_t& k) {
return H::combine(std::move(h), k.digest);
}
};
key_t make_key(
scram_algorithm_t mech,
const credential_password& password,
bytes_view salt,
int iterations) const;
bytes _digest_key;
utils::chunked_kv_cache<key_t, bytes> _cache;
};
} // namespace security