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