xrpld
Loading...
Searching...
No Matches
Manifest.h
1#pragma once
2
3#include <xrpl/basics/Blob.h>
4#include <xrpl/basics/Slice.h>
5#include <xrpl/basics/UnorderedContainers.h>
6#include <xrpl/basics/base64.h>
7#include <xrpl/basics/base_uint.h>
8#include <xrpl/beast/utility/Journal.h>
9#include <xrpl/protocol/PublicKey.h>
10#include <xrpl/protocol/SecretKey.h>
11
12#include <atomic>
13#include <cstddef>
14#include <cstdint>
15#include <functional>
16#include <optional>
17#include <shared_mutex>
18#include <string>
19#include <type_traits>
20#include <utility>
21#include <vector>
22
23namespace xrpl {
24
25/*
26 Validator key manifests
27 -----------------------
28
29 Suppose the secret keys installed on an XRPL validator are compromised. Not
30 only do you have to generate and install new key pairs on each validator,
31 EVERY xrpld needs to have its config updated with the new public keys, and
32 is vulnerable to forged validation signatures until this is done. The
33 solution is a new layer of indirection: A master secret key under
34 restrictive access control is used to sign a "manifest": essentially, a
35 certificate including the master public key, an ephemeral public key for
36 verifying validations (which will be signed by its secret counterpart), a
37 sequence number, and a digital signature.
38
39 The manifest has two serialized forms: one which includes the digital
40 signature and one which doesn't. There is an obvious causal dependency
41 relationship between the (latter) form with no signature, the signature
42 of that form, and the (former) form which includes that signature. In
43 other words, a message can't contain a signature of itself. The code
44 below stores a serialized manifest which includes the signature, and
45 dynamically generates the signatureless form when it needs to verify
46 the signature.
47
48 An instance of ManifestCache stores, for each known validator, (a) its
49 master public key, and (b) the most senior of all valid manifests it has
50 seen for that validator, if any. On startup, the [validator_token] config
51 entry (which contains the manifest for this validator) is decoded and
52 added to the manifest cache. Other manifests are added as "gossip"
53 received from xrpld peers, including ones for validators this node does not
54 trust. Manifests for untrusted validators are capped (kMaxUntrustedCount)
55 so peer gossip cannot grow the cache without bound; trusted validators are
56 not capped. Entries are never evicted, so a stored revocation is permanent.
57
58 When an ephemeral key is compromised, a new signing key pair is created,
59 along with a new manifest vouching for it (with a higher sequence number),
60 signed by the master key. When an xrpld peer receives the new manifest,
61 it verifies it with the master key and (assuming it's valid) discards the
62 old ephemeral key and stores the new one. If the master key itself gets
63 compromised, a manifest with sequence number 0xFFFFFFFF will supersede a
64 prior manifest and discard any existing ephemeral key without storing a
65 new one. These revocation manifests are loaded from the
66 [validator_key_revocation] config entry as well as received as gossip from
67 peers. Since no further manifests for this master key will be accepted
68 (since no higher sequence number is possible), and no signing key is on
69 record, no validations will be accepted from the compromised validator.
70*/
71
72//------------------------------------------------------------------------------
73
75{
80
85
89 // A revoked manifest does not have a signingKey
90 // This field is specified as "optional" in manifestFormat's
91 // SOTemplate
93
98
103
104 Manifest() = delete;
105
119
120 Manifest(Manifest const& other) = delete;
121 Manifest&
122 operator=(Manifest const& other) = delete;
123 Manifest(Manifest&& other) = default;
124 Manifest&
125 operator=(Manifest&& other) = default;
126
130 [[nodiscard]] bool
131 verify() const;
132
136 [[nodiscard]] uint256
137 hash() const;
138
142 // The maximum possible sequence number means that the master key has
143 // been revoked
144 static bool
146
150 [[nodiscard]] bool
151 revoked() const;
152
156 [[nodiscard]] std::optional<Blob>
157 getSignature() const;
158
162 [[nodiscard]] Blob
163 getMasterSignature() const;
164};
165
170to_string(Manifest const& m);
171
192
202
217
233
245constexpr std::size_t
247{
248 return configured.value_or(kMaxUntrustedCount);
249}
250
260constexpr std::size_t
262{
263 return configured.value_or(kMaxTrustedCount);
264}
265
278deserializeManifest(Slice s, beast::Journal journal);
279
287
288template <class T>
297
298
299inline bool
300operator==(Manifest const& lhs, Manifest const& rhs)
301{
302 // In theory, comparing the two serialized strings should be
303 // sufficient.
304 return lhs.sequence == rhs.sequence && lhs.masterKey == rhs.masterKey &&
305 lhs.signingKey == rhs.signingKey && lhs.domain == rhs.domain &&
306 lhs.serialized == rhs.serialized;
307}
308
314
317 std::vector<std::string> const& blob,
319
333
334inline std::string
336{
337 switch (m)
338 {
340 return "accepted";
342 return "stale";
344 return "badMasterKey";
346 return "badEphemeralKey";
348 return "invalid";
350 return "untrustedCapacity";
351 default:
352 return "unknown";
353 }
354}
355
367
368class DatabaseCon;
369
374{
375private:
378
383
388
390
400
408
416
422 static constexpr std::uint64_t kUntrustedRejectCount = 10000;
423
424public:
434 std::size_t maxUntrustedCount = kMaxUntrustedCount)
435 : j_(j), maxUntrustedCount_(maxUntrustedCount)
436 {
437 }
438
443 sequence() const
444 {
445 return seq_.load();
446 }
447
460 getSigningKey(PublicKey const& pk) const;
461
474 getMasterKey(PublicKey const& pk) const;
475
483 getSequence(PublicKey const& pk) const;
484
492 getDomain(PublicKey const& pk) const;
493
501 getManifest(PublicKey const& pk) const;
502
512 bool
513 revoked(PublicKey const& pk) const;
514
541
554 void
555 promoteToTrusted(PublicKey const& pk);
556
574 bool
575 load(
576 DatabaseCon& dbCon,
577 std::string const& dbTable,
578 std::string const& configManifest,
579 std::vector<std::string> const& configRevocation);
580
592 void
593 load(DatabaseCon& dbCon, std::string const& dbTable);
594
606 void
607 save(
608 DatabaseCon& dbCon,
609 std::string const& dbTable,
610 std::function<bool(PublicKey const&)> const& isTrusted);
611
626 template <class Function>
627 void
628 forEachManifest(Function&& f) const
629 {
630 std::shared_lock const lock{mutex_};
631 for (auto const& [_, manifest] : map_)
632 {
633 (void)_;
634 f(manifest);
635 }
636 }
637
655 template <class PreFun, class EachFun>
656 void
657 forEachManifest(PreFun&& pf, EachFun&& f) const
658 {
659 std::shared_lock const lock{mutex_};
660 pf(map_.size());
661 for (auto const& [_, manifest] : map_)
662 {
663 (void)_;
664 f(manifest);
665 }
666 }
667};
668
669} // namespace xrpl
A generic endpoint for log messages.
Definition Journal.h:44
static Sink & getNullSink()
Returns a Sink which does nothing.
void forEachManifest(Function &&f) const
Invokes the callback once for every populated manifest.
Definition Manifest.h:628
std::size_t const maxUntrustedCount_
Maximum number of untrusted master keys kept in the cache.
Definition Manifest.h:407
std::atomic< std::uint32_t > seq_
Definition Manifest.h:389
std::shared_mutex mutex_
Definition Manifest.h:377
bool load(DatabaseCon &dbCon, std::string const &dbTable, std::string const &configManifest, std::vector< std::string > const &configRevocation)
Populate manifest cache with manifests in database and config.
std::optional< PublicKey > getSigningKey(PublicKey const &pk) const
Returns master key's current signing key.
ManifestDisposition applyManifest(Manifest m, ManifestRateLimitCapPolicy cap)
Add manifest to cache.
static constexpr std::uint64_t kUntrustedRejectCount
Number of cap rejections between summary warnings.
Definition Manifest.h:422
hash_set< PublicKey > untrustedKeys_
Master keys of cached manifests for validators this node does not list.
Definition Manifest.h:399
std::optional< std::string > getDomain(PublicKey const &pk) const
Returns domain claimed by a given public key.
ManifestCache(beast::Journal j=beast::Journal(beast::Journal::getNullSink()), std::size_t maxUntrustedCount=kMaxUntrustedCount)
Definition Manifest.h:432
PublicKey getMasterKey(PublicKey const &pk) const
Returns ephemeral signing key's master public key.
std::uint32_t sequence() const
A monotonically increasing number used to detect new manifests.
Definition Manifest.h:443
hash_map< PublicKey, PublicKey > signingToMasterKeys_
Master public keys stored by current ephemeral public key.
Definition Manifest.h:387
std::optional< std::string > getManifest(PublicKey const &pk) const
Returns manifest corresponding to a given public key.
hash_map< PublicKey, Manifest > map_
Active manifests stored by master public key.
Definition Manifest.h:382
std::optional< std::uint32_t > getSequence(PublicKey const &pk) const
Returns master key's current manifest sequence.
void save(DatabaseCon &dbCon, std::string const &dbTable, std::function< bool(PublicKey const &)> const &isTrusted)
Save cached manifests to database.
std::atomic< std::uint64_t > untrustedRejectCount_
Running count of manifests rejected because the untrusted cap was full.
Definition Manifest.h:415
void forEachManifest(PreFun &&pf, EachFun &&f) const
Invokes the callback once for every populated manifest.
Definition Manifest.h:657
beast::Journal j_
Definition Manifest.h:376
void promoteToTrusted(PublicKey const &pk)
Stop counting a master key against the untrusted cap.
bool revoked(PublicKey const &pk) const
Returns true if master key has been revoked in a manifest.
A public key.
Definition PublicKey.h:53
A secret key.
Definition SecretKey.h:24
T is_same_v
STL namespace.
constexpr std::size_t encodedSize(std::size_t const nBytes)
Returns the maximum number of characters needed to base64-encode nBytes bytes.
Definition base64.h:53
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
constexpr std::size_t kMaxTrustedCount
Default number of trusted manifests allowed in a Manifest message.
Definition Manifest.h:232
constexpr bool operator==(BaseUInt< Bits, Tag > const &lhs, BaseUInt< Bits, Tag > const &rhs)
Definition base_uint.h:606
constexpr std::size_t kMaxManifestBytes
Largest a valid manifest can be, in decoded bytes.
Definition Manifest.h:191
std::unordered_set< Value, Hash, Pred, Allocator > hash_set
constexpr std::size_t kMaxManifestBase64
Largest a valid manifest can be, in base64 characters.
Definition Manifest.h:201
constexpr std::size_t kMaxUntrustedCount
Default number of untrusted manifests to store in cache and allowed in one Manifest message.
Definition Manifest.h:216
std::string to_string(BaseUInt< Bits, Tag > const &a)
Definition base_uint.h:651
@ Stale
Not current or was older than current from this node.
std::optional< Manifest > deserializeManifest(Slice s, beast::Journal journal)
Constructs Manifest from serialized string.
ManifestRateLimitCapPolicy
Whether a manifest counts against the 'untrusted' cache cap.
Definition Manifest.h:363
@ Uncapped
Bypasses the cap (listed/trusted or config manifests).
Definition Manifest.h:365
@ Capped
Subject to the untrusted cap (unlisted peer gossip).
Definition Manifest.h:364
Slice makeSlice(std::array< T, N > const &a)
Definition Slice.h:228
@ Accepted
We have accepted a new last closed ledger and are waiting on a call to startRound to begin the next c...
constexpr std::size_t trustedManifestCount(std::optional< std::size_t > const &configured)
Number of trusted manifests allowed in a Manifest message.
Definition Manifest.h:261
std::unordered_map< Key, Value, Hash, Pred, Allocator > hash_map
constexpr std::size_t untrustedManifestCount(std::optional< std::size_t > const &configured)
Number of untrusted manifests to store in cache and allowed in one Manifest message.
Definition Manifest.h:246
std::vector< unsigned char > Blob
Storage for linear binary data.
Definition Blob.h:11
BaseUInt< 256 > uint256
Definition base_uint.h:580
std::optional< ValidatorToken > loadValidatorToken(std::vector< std::string > const &blob, beast::Journal journal=beast::Journal(beast::Journal::getNullSink()))
ManifestDisposition
Definition Manifest.h:320
@ BadMasterKey
The master key is not acceptable to us.
Definition Manifest.h:325
@ Accepted
Manifest is valid.
Definition Manifest.h:321
@ Invalid
Timely, but invalid signature.
Definition Manifest.h:329
@ BadEphemeralKey
The ephemeral key is not acceptable to us.
Definition Manifest.h:327
@ Stale
Sequence is too old.
Definition Manifest.h:323
@ UntrustedCapacity
Unlisted and limit reached.
Definition Manifest.h:331
Manifest(Manifest const &other)=delete
Manifest(std::string serialized, PublicKey const &masterKey, std::optional< PublicKey > const &signingKey, std::uint32_t seq, std::string domain)
Definition Manifest.h:106
Manifest & operator=(Manifest const &other)=delete
PublicKey masterKey
The master key associated with this manifest.
Definition Manifest.h:84
Manifest(Manifest &&other)=default
std::string serialized
The manifest in serialized form.
Definition Manifest.h:79
Blob getMasterSignature() const
Returns manifest master key signature.
std::string domain
The domain, if one was specified in the manifest; empty otherwise.
Definition Manifest.h:102
std::optional< Blob > getSignature() const
Returns manifest signature.
std::optional< PublicKey > signingKey
The ephemeral key associated with this manifest.
Definition Manifest.h:92
std::uint32_t sequence
The sequence number of this manifest.
Definition Manifest.h:97
bool revoked() const
Returns true if manifest revokes master key.
uint256 hash() const
Returns hash of serialized manifest data.
bool verify() const
Returns true if manifest signature is valid.
Manifest & operator=(Manifest &&other)=default
Manifest()=delete
std::string manifest
Definition Manifest.h:311
SecretKey validationSecret
Definition Manifest.h:312
T value_or(T... args)