rippled
Loading...
Searching...
No Matches
app/misc/detail/Manifest.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpld/app/misc/Manifest.h>
21#include <xrpld/app/rdb/Wallet.h>
22#include <xrpld/core/DatabaseCon.h>
23#include <xrpl/basics/Log.h>
24#include <xrpl/basics/StringUtilities.h>
25#include <xrpl/basics/base64.h>
26#include <xrpl/json/json_reader.h>
27#include <xrpl/protocol/PublicKey.h>
28#include <xrpl/protocol/Sign.h>
29
30#include <boost/algorithm/string/trim.hpp>
31
32#include <numeric>
33#include <stdexcept>
34
35namespace ripple {
36
39{
40 auto const mk = toBase58(TokenType::NodePublic, m.masterKey);
41
42 if (m.revoked())
43 return "Revocation Manifest " + mk;
44
45 if (!m.signingKey)
46 Throw<std::runtime_error>("No SigningKey in manifest " + mk);
47
48 return "Manifest " + mk + " (" + std::to_string(m.sequence) + ": " +
50}
51
54{
55 if (s.empty())
56 return std::nullopt;
57
58 static SOTemplate const manifestFormat{
59 // A manifest must include:
60 // - the master public key
61 {sfPublicKey, soeREQUIRED},
62
63 // - a signature with that public key
64 {sfMasterSignature, soeREQUIRED},
65
66 // - a sequence number
67 {sfSequence, soeREQUIRED},
68
69 // It may, optionally, contain:
70 // - a version number which defaults to 0
71 {sfVersion, soeDEFAULT},
72
73 // - a domain name
74 {sfDomain, soeOPTIONAL},
75
76 // - an ephemeral signing key that can be changed as necessary
77 {sfSigningPubKey, soeOPTIONAL},
78
79 // - a signature using the ephemeral signing key, if it is present
80 {sfSignature, soeOPTIONAL},
81 };
82
83 try
84 {
85 SerialIter sit{s};
86 STObject st{sit, sfGeneric};
87
88 st.applyTemplate(manifestFormat);
89
90 // We only understand "version 0" manifests at this time:
91 if (st.isFieldPresent(sfVersion) && st.getFieldU16(sfVersion) != 0)
92 return std::nullopt;
93
94 auto const pk = st.getFieldVL(sfPublicKey);
95
96 if (!publicKeyType(makeSlice(pk)))
97 return std::nullopt;
98
99 PublicKey const masterKey = PublicKey(makeSlice(pk));
100 std::uint32_t const seq = st.getFieldU32(sfSequence);
101
102 std::string domain;
103
104 std::optional<PublicKey> signingKey;
105
106 if (st.isFieldPresent(sfDomain))
107 {
108 auto const d = st.getFieldVL(sfDomain);
109
110 domain.assign(reinterpret_cast<char const*>(d.data()), d.size());
111
112 if (!isProperlyFormedTomlDomain(domain))
113 return std::nullopt;
114 }
115
116 bool const hasEphemeralKey = st.isFieldPresent(sfSigningPubKey);
117 bool const hasEphemeralSig = st.isFieldPresent(sfSignature);
118
119 if (Manifest::revoked(seq))
120 {
121 // Revocation manifests should not specify a new signing key
122 // or a signing key signature.
123 if (hasEphemeralKey)
124 return std::nullopt;
125
126 if (hasEphemeralSig)
127 return std::nullopt;
128 }
129 else
130 {
131 // Regular manifests should contain a signing key and an
132 // associated signature.
133 if (!hasEphemeralKey)
134 return std::nullopt;
135
136 if (!hasEphemeralSig)
137 return std::nullopt;
138
139 auto const spk = st.getFieldVL(sfSigningPubKey);
140
141 if (!publicKeyType(makeSlice(spk)))
142 return std::nullopt;
143
144 signingKey.emplace(makeSlice(spk));
145
146 // The signing and master keys can't be the same
147 if (*signingKey == masterKey)
148 return std::nullopt;
149 }
150
151 std::string const serialized(
152 reinterpret_cast<char const*>(s.data()), s.size());
153
154 // If the manifest is revoked, then the signingKey will be unseated
155 return Manifest(serialized, masterKey, signingKey, seq, domain);
156 }
157 catch (std::exception const& ex)
158 {
159 JLOG(journal.error())
160 << "Exception in " << __func__ << ": " << ex.what();
161 return std::nullopt;
162 }
163}
164
165template <class Stream>
166Stream&
168 Stream& s,
169 std::string const& action,
170 PublicKey const& pk,
171 std::uint32_t seq)
172{
173 s << "Manifest: " << action
174 << ";Pk: " << toBase58(TokenType::NodePublic, pk) << ";Seq: " << seq
175 << ";";
176 return s;
177}
178
179template <class Stream>
180Stream&
182 Stream& s,
183 std::string const& action,
184 PublicKey const& pk,
185 std::uint32_t seq,
186 std::uint32_t oldSeq)
187{
188 s << "Manifest: " << action
189 << ";Pk: " << toBase58(TokenType::NodePublic, pk) << ";Seq: " << seq
190 << ";OldSeq: " << oldSeq << ";";
191 return s;
192}
193
194bool
196{
199 st.set(sit);
200
201 // The manifest must either have a signing key or be revoked. This check
202 // prevents us from accessing an unseated signingKey in the next check.
203 if (!revoked() && !signingKey)
204 return false;
205
206 // Signing key and signature are not required for
207 // master key revocations
209 return false;
210
211 return ripple::verify(
212 st, HashPrefix::manifest, masterKey, sfMasterSignature);
213}
214
217{
220 st.set(sit);
221 return st.getHash(HashPrefix::manifest);
222}
223
224bool
226{
227 /*
228 The maximum possible sequence number means that the master key
229 has been revoked.
230 */
231 return revoked(sequence);
232}
233
234bool
236{
237 // The maximum possible sequence number means that the master key has
238 // been revoked.
240}
241
244{
247 st.set(sit);
248 if (!get(st, sfSignature))
249 return std::nullopt;
250 return st.getFieldVL(sfSignature);
251}
252
253Blob
255{
258 st.set(sit);
259 return st.getFieldVL(sfMasterSignature);
260}
261
264{
265 try
266 {
267 std::string tokenStr;
268
269 tokenStr.reserve(std::accumulate(
270 blob.cbegin(),
271 blob.cend(),
272 std::size_t(0),
273 [](std::size_t init, std::string const& s) {
274 return init + s.size();
275 }));
276
277 for (auto const& line : blob)
278 tokenStr += boost::algorithm::trim_copy(line);
279
280 tokenStr = base64_decode(tokenStr);
281
282 Json::Reader r;
283 Json::Value token;
284
285 if (r.parse(tokenStr, token))
286 {
287 auto const m = token.get("manifest", Json::Value{});
288 auto const k = token.get("validation_secret_key", Json::Value{});
289
290 if (m.isString() && k.isString())
291 {
292 auto const key = strUnHex(k.asString());
293
294 if (key && key->size() == 32)
295 return ValidatorToken{m.asString(), makeSlice(*key)};
296 }
297 }
298
299 return std::nullopt;
300 }
301 catch (std::exception const& ex)
302 {
303 JLOG(journal.error())
304 << "Exception in " << __func__ << ": " << ex.what();
305 return std::nullopt;
306 }
307}
308
311{
313 auto const iter = map_.find(pk);
314
315 if (iter != map_.end() && !iter->second.revoked())
316 return iter->second.signingKey;
317
318 return pk;
319}
320
323{
325
326 if (auto const iter = signingToMasterKeys_.find(pk);
327 iter != signingToMasterKeys_.end())
328 return iter->second;
329
330 return pk;
331}
332
335{
337 auto const iter = map_.find(pk);
338
339 if (iter != map_.end() && !iter->second.revoked())
340 return iter->second.sequence;
341
342 return std::nullopt;
343}
344
347{
349 auto const iter = map_.find(pk);
350
351 if (iter != map_.end() && !iter->second.revoked())
352 return iter->second.domain;
353
354 return std::nullopt;
355}
356
359{
361 auto const iter = map_.find(pk);
362
363 if (iter != map_.end() && !iter->second.revoked())
364 return iter->second.serialized;
365
366 return std::nullopt;
367}
368
369bool
371{
373 auto const iter = map_.find(pk);
374
375 if (iter != map_.end())
376 return iter->second.revoked();
377
378 return false;
379}
380
383{
384 // Check the manifest against the conditions that do not require a
385 // `unique_lock` (write lock) on the `mutex_`. Since the signature can be
386 // relatively expensive, the `checkSignature` parameter determines if the
387 // signature should be checked. Since `prewriteCheck` is run twice (see
388 // comment below), `checkSignature` only needs to be set to true on the
389 // first run.
390 auto prewriteCheck =
391 [this, &m](auto const& iter, bool checkSignature, auto const& lock)
393 XRPL_ASSERT(
394 lock.owns_lock(),
395 "ripple::ManifestCache::applyManifest::prewriteCheck : locked");
396 (void)lock; // not used. parameter is present to ensure the mutex is
397 // locked when the lambda is called.
398 if (iter != map_.end() && m.sequence <= iter->second.sequence)
399 {
400 // We received a manifest whose sequence number is not strictly
401 // greater than the one we already know about. This can happen in
402 // several cases including when we receive manifests from a peer who
403 // doesn't have the latest data.
404 if (auto stream = j_.debug())
405 logMftAct(
406 stream,
407 "Stale",
408 m.masterKey,
409 m.sequence,
410 iter->second.sequence);
412 }
413
414 if (checkSignature && !m.verify())
415 {
416 if (auto stream = j_.warn())
417 logMftAct(stream, "Invalid", m.masterKey, m.sequence);
419 }
420
421 // If the master key associated with a manifest is or might be
422 // compromised and is, therefore, no longer trustworthy.
423 //
424 // A manifest revocation essentially marks a manifest as compromised. By
425 // setting the sequence number to the highest value possible, the
426 // manifest is effectively neutered and cannot be superseded by a forged
427 // one.
428 bool const revoked = m.revoked();
429
430 if (auto stream = j_.warn(); stream && revoked)
431 logMftAct(stream, "Revoked", m.masterKey, m.sequence);
432
433 // Sanity check: the master key of this manifest should not be used as
434 // the ephemeral key of another manifest:
435 if (auto const x = signingToMasterKeys_.find(m.masterKey);
436 x != signingToMasterKeys_.end())
437 {
438 JLOG(j_.warn()) << to_string(m)
439 << ": Master key already used as ephemeral key for "
440 << toBase58(TokenType::NodePublic, x->second);
441
443 }
444
445 if (!revoked)
446 {
447 if (!m.signingKey)
448 {
449 JLOG(j_.warn()) << to_string(m)
450 << ": is not revoked and the manifest has no "
451 "signing key. Hence, the manifest is "
452 "invalid";
454 }
455
456 // Sanity check: the ephemeral key of this manifest should not be
457 // used as the master or ephemeral key of another manifest:
458 if (auto const x = signingToMasterKeys_.find(*m.signingKey);
459 x != signingToMasterKeys_.end())
460 {
461 JLOG(j_.warn())
462 << to_string(m)
463 << ": Ephemeral key already used as ephemeral key for "
464 << toBase58(TokenType::NodePublic, x->second);
465
467 }
468
469 if (auto const x = map_.find(*m.signingKey); x != map_.end())
470 {
471 JLOG(j_.warn())
472 << to_string(m) << ": Ephemeral key used as master key for "
473 << to_string(x->second);
474
476 }
477 }
478
479 return std::nullopt;
480 };
481
482 {
484 if (auto d =
485 prewriteCheck(map_.find(m.masterKey), /*checkSig*/ true, sl))
486 return *d;
487 }
488
490 auto const iter = map_.find(m.masterKey);
491 // Since we released the previously held read lock, it's possible that the
492 // collections have been written to. This means we need to run
493 // `prewriteCheck` again. This re-does work, but `prewriteCheck` is
494 // relatively inexpensive to run, and doing it this way allows us to run
495 // `prewriteCheck` under a `shared_lock` above.
496 // Note, the signature has already been checked above, so it
497 // doesn't need to happen again (signature checks are somewhat expensive).
498 // Note: It's a mistake to use an upgradable lock. This is a recipe for
499 // deadlock.
500 if (auto d = prewriteCheck(iter, /*checkSig*/ false, sl))
501 return *d;
502
503 bool const revoked = m.revoked();
504 // This is the first manifest we are seeing for a master key. This should
505 // only ever happen once per validator run.
506 if (iter == map_.end())
507 {
508 if (auto stream = j_.info())
509 logMftAct(stream, "AcceptedNew", m.masterKey, m.sequence);
510
511 if (!revoked)
513
514 auto masterKey = m.masterKey;
515 map_.emplace(std::move(masterKey), std::move(m));
517 }
518
519 // An ephemeral key was revoked and superseded by a new key. This is
520 // expected, but should happen infrequently.
521 if (auto stream = j_.info())
522 logMftAct(
523 stream,
524 "AcceptedUpdate",
525 m.masterKey,
526 m.sequence,
527 iter->second.sequence);
528
529 signingToMasterKeys_.erase(*iter->second.signingKey);
530
531 if (!revoked)
533
534 iter->second = std::move(m);
535
536 // Something has changed. Keep track of it.
537 seq_++;
538
540}
541
542void
544{
545 auto db = dbCon.checkoutDb();
546 ripple::getManifests(*db, dbTable, *this, j_);
547}
548
549bool
551 DatabaseCon& dbCon,
552 std::string const& dbTable,
553 std::string const& configManifest,
554 std::vector<std::string> const& configRevocation)
555{
556 load(dbCon, dbTable);
557
558 if (!configManifest.empty())
559 {
560 auto mo = deserializeManifest(base64_decode(configManifest));
561 if (!mo)
562 {
563 JLOG(j_.error()) << "Malformed validator_token in config";
564 return false;
565 }
566
567 if (mo->revoked())
568 {
569 JLOG(j_.warn()) << "Configured manifest revokes public key";
570 }
571
572 if (applyManifest(std::move(*mo)) == ManifestDisposition::invalid)
573 {
574 JLOG(j_.error()) << "Manifest in config was rejected";
575 return false;
576 }
577 }
578
579 if (!configRevocation.empty())
580 {
581 std::string revocationStr;
582 revocationStr.reserve(std::accumulate(
583 configRevocation.cbegin(),
584 configRevocation.cend(),
585 std::size_t(0),
586 [](std::size_t init, std::string const& s) {
587 return init + s.size();
588 }));
589
590 for (auto const& line : configRevocation)
591 revocationStr += boost::algorithm::trim_copy(line);
592
593 auto mo = deserializeManifest(base64_decode(revocationStr));
594
595 if (!mo || !mo->revoked() ||
597 {
598 JLOG(j_.error()) << "Invalid validator key revocation in config";
599 return false;
600 }
601 }
602
603 return true;
604}
605
606void
608 DatabaseCon& dbCon,
609 std::string const& dbTable,
610 std::function<bool(PublicKey const&)> const& isTrusted)
611{
613 auto db = dbCon.checkoutDb();
614
615 saveManifests(*db, dbTable, isTrusted, map_, j_);
616}
617} // namespace ripple
T accumulate(T... args)
T assign(T... args)
T cbegin(T... args)
Unserialize a JSON document into a Value.
Definition: json_reader.h:37
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Definition: json_reader.cpp:73
Represents a JSON value.
Definition: json_value.h:147
Value get(UInt index, const Value &defaultValue) const
If the array contains at least index+1 elements, returns the element value, otherwise returns default...
Definition: json_value.cpp:841
A generic endpoint for log messages.
Definition: Journal.h:59
Stream error() const
Definition: Journal.h:335
Stream debug() const
Definition: Journal.h:317
Stream info() const
Definition: Journal.h:323
Stream warn() const
Definition: Journal.h:329
LockedSociSession checkoutDb()
Definition: DatabaseCon.h:188
std::optional< std::uint32_t > getSequence(PublicKey const &pk) const
Returns master key's current manifest sequence.
hash_map< PublicKey, PublicKey > signingToMasterKeys_
Master public keys stored by current ephemeral public key.
Definition: Manifest.h:265
std::optional< std::string > getManifest(PublicKey const &pk) const
Returns mainfest corresponding to a given public key.
std::optional< PublicKey > getSigningKey(PublicKey const &pk) const
Returns master key's current signing key.
std::shared_mutex mutex_
Definition: Manifest.h:259
bool revoked(PublicKey const &pk) const
Returns true if master key has been revoked in a manifest.
beast::Journal j_
Definition: Manifest.h:258
ManifestDisposition applyManifest(Manifest m)
Add manifest to cache.
hash_map< PublicKey, Manifest > map_
Active manifests stored by master public key.
Definition: Manifest.h:262
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::atomic< std::uint32_t > seq_
Definition: Manifest.h:267
void save(DatabaseCon &dbCon, std::string const &dbTable, std::function< bool(PublicKey const &)> const &isTrusted)
Save cached manifests to database.
PublicKey getMasterKey(PublicKey const &pk) const
Returns ephemeral signing key's master public key.
std::optional< std::string > getDomain(PublicKey const &pk) const
Returns domain claimed by a given public key.
A public key.
Definition: PublicKey.h:62
Defines the fields and their attributes within a STObject.
Definition: SOTemplate.h:113
Blob getFieldVL(SField const &field) const
Definition: STObject.cpp:627
uint256 getHash(HashPrefix prefix) const
Definition: STObject.cpp:365
void set(const SOTemplate &)
Definition: STObject.cpp:126
An immutable linear range of bytes.
Definition: Slice.h:45
bool empty() const noexcept
Return true if the byte range is empty.
Definition: Slice.h:69
std::uint8_t const * data() const noexcept
Return a pointer to beginning of the storage.
Definition: Slice.h:97
std::size_t size() const noexcept
Returns the number of bytes in the storage.
Definition: Slice.h:80
T data(T... args)
T emplace(T... args)
T empty(T... args)
T cend(T... args)
T max(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:106
std::optional< Manifest > deserializeManifest(Slice s, beast::Journal journal)
Constructs Manifest from serialized string.
std::optional< Blob > strUnHex(std::size_t strSize, Iterator begin, Iterator end)
void saveManifests(soci::session &session, std::string const &dbTable, std::function< bool(PublicKey const &)> const &isTrusted, hash_map< PublicKey, Manifest > const &map, beast::Journal j)
saveManifests Saves all given manifests to the database.
Definition: Wallet.cpp:93
bool verify(PublicKey const &publicKey, Slice const &m, Slice const &sig, bool mustBeFullyCanonical=true) noexcept
Verify a signature on a message.
Definition: PublicKey.cpp:272
std::string base64_decode(std::string_view data)
Definition: base64.cpp:245
SField const sfGeneric
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
Definition: PublicKey.cpp:207
Stream & logMftAct(Stream &s, std::string const &action, PublicKey const &pk, std::uint32_t seq)
ManifestDisposition
Definition: Manifest.h:215
@ badMasterKey
The master key is not acceptable to us.
@ stale
Sequence is too old.
@ accepted
Manifest is valid.
@ badEphemeralKey
The ephemeral key is not acceptable to us.
@ invalid
Timely, but invalid signature.
std::enable_if_t< std::is_same< T, char >::value||std::is_same< T, unsigned char >::value, Slice > makeSlice(std::array< T, N > const &a)
Definition: Slice.h:243
std::string to_string(base_uint< Bits, Tag > const &a)
Definition: base_uint.h:629
T get(Section const &section, std::string const &name, T const &defaultValue=T{})
Retrieve a key/value pair from a section.
Definition: BasicConfig.h:356
std::optional< ValidatorToken > loadValidatorToken(std::vector< std::string > const &blob, beast::Journal journal)
@ manifest
Manifest.
@ soeOPTIONAL
Definition: SOTemplate.h:36
@ soeREQUIRED
Definition: SOTemplate.h:35
@ soeDEFAULT
Definition: SOTemplate.h:37
bool isProperlyFormedTomlDomain(std::string_view domain)
Determines if the given string looks like a TOML-file hosting domain.
void getManifests(soci::session &session, std::string const &dbTable, ManifestCache &mCache, beast::Journal j)
getManifests Loads a manifest from the wallet database and stores it in the cache.
Definition: Wallet.cpp:45
T reserve(T... args)
T size(T... args)
std::string serialized
The manifest in serialized form.
Definition: Manifest.h:83
std::uint32_t sequence
The sequence number of this manifest.
Definition: Manifest.h:95
static bool revoked(std::uint32_t sequence)
Returns true if manifest revokes master key.
bool revoked() const
Returns true if manifest revokes master key.
bool verify() const
Returns true if manifest signature is valid.
uint256 hash() const
Returns hash of serialized manifest data.
std::optional< Blob > getSignature() const
Returns manifest signature.
std::optional< PublicKey > signingKey
The ephemeral key associated with this manifest.
Definition: Manifest.h:92
Blob getMasterSignature() const
Returns manifest master key signature.
PublicKey masterKey
The master key associated with this manifest.
Definition: Manifest.h:86
T to_string(T... args)
T what(T... args)