rippled
app/misc/impl/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 <ripple/app/misc/Manifest.h>
21 #include <ripple/basics/Log.h>
22 #include <ripple/basics/StringUtilities.h>
23 #include <ripple/basics/base64.h>
24 #include <ripple/basics/contract.h>
25 #include <ripple/core/DatabaseCon.h>
26 #include <ripple/json/json_reader.h>
27 #include <ripple/protocol/PublicKey.h>
28 #include <ripple/protocol/Sign.h>
29 #include <boost/algorithm/clamp.hpp>
30 #include <boost/algorithm/string/trim.hpp>
31 #include <boost/regex.hpp>
32 #include <numeric>
33 #include <stdexcept>
34 
35 namespace ripple {
36 
37 boost::optional<Manifest>
39 {
40  if (s.empty())
41  return boost::none;
42 
43  static SOTemplate const manifestFormat{
44  // A manifest must include:
45  // - the master public key
47 
48  // - a signature with that public key
50 
51  // - a sequence number
53 
54  // It may, optionally, contain:
55  // - a version number which defaults to 0
57 
58  // - a domain name
60 
61  // - an ephemeral signing key that can be changed as necessary
63 
64  // - a signature using the ephemeral signing key, if it is present
66  };
67 
68  try
69  {
70  SerialIter sit{s};
71  STObject st{sit, sfGeneric};
72 
73  st.applyTemplate(manifestFormat);
74 
75  // We only understand "version 0" manifests at this time:
76  if (st.isFieldPresent(sfVersion) && st.getFieldU16(sfVersion) != 0)
77  return boost::none;
78 
79  auto const pk = st.getFieldVL(sfPublicKey);
80 
81  if (!publicKeyType(makeSlice(pk)))
82  return boost::none;
83 
84  Manifest m;
85  m.serialized.assign(reinterpret_cast<char const*>(s.data()), s.size());
86  m.masterKey = PublicKey(makeSlice(pk));
87  m.sequence = st.getFieldU32(sfSequence);
88 
89  if (st.isFieldPresent(sfDomain))
90  {
91  auto const d = st.getFieldVL(sfDomain);
92 
93  m.domain.assign(reinterpret_cast<char const*>(d.data()), d.size());
94 
96  return boost::none;
97  }
98 
99  bool const hasEphemeralKey = st.isFieldPresent(sfSigningPubKey);
100  bool const hasEphemeralSig = st.isFieldPresent(sfSignature);
101 
102  if (m.revoked())
103  {
104  // Revocation manifests should not specify a new signing key
105  // or a signing key signature.
106  if (hasEphemeralKey)
107  return boost::none;
108 
109  if (hasEphemeralSig)
110  return boost::none;
111  }
112  else
113  {
114  // Regular manifests should contain a signing key and an
115  // associated signature.
116  if (!hasEphemeralKey)
117  return boost::none;
118 
119  if (!hasEphemeralSig)
120  return boost::none;
121 
122  auto const spk = st.getFieldVL(sfSigningPubKey);
123 
124  if (!publicKeyType(makeSlice(spk)))
125  return boost::none;
126 
127  m.signingKey = PublicKey(makeSlice(spk));
128  }
129 
130  return m;
131  }
132  catch (std::exception const&)
133  {
134  return boost::none;
135  }
136 }
137 
138 template <class Stream>
139 Stream&
141  Stream& s,
142  std::string const& action,
143  PublicKey const& pk,
144  std::uint32_t seq)
145 {
146  s << "Manifest: " << action
147  << ";Pk: " << toBase58(TokenType::NodePublic, pk) << ";Seq: " << seq
148  << ";";
149  return s;
150 }
151 
152 template <class Stream>
153 Stream&
155  Stream& s,
156  std::string const& action,
157  PublicKey const& pk,
158  std::uint32_t seq,
159  std::uint32_t oldSeq)
160 {
161  s << "Manifest: " << action
162  << ";Pk: " << toBase58(TokenType::NodePublic, pk) << ";Seq: " << seq
163  << ";OldSeq: " << oldSeq << ";";
164  return s;
165 }
166 
167 bool
169 {
170  STObject st(sfGeneric);
172  st.set(sit);
173 
174  // Signing key and signature are not required for
175  // master key revocations
177  return false;
178 
179  return ripple::verify(
181 }
182 
183 uint256
185 {
186  STObject st(sfGeneric);
188  st.set(sit);
189  return st.getHash(HashPrefix::manifest);
190 }
191 
192 bool
194 {
195  /*
196  The maximum possible sequence number means that the master key
197  has been revoked.
198  */
200 }
201 
202 boost::optional<Blob>
204 {
205  STObject st(sfGeneric);
207  st.set(sit);
208  if (!get(st, sfSignature))
209  return boost::none;
210  return st.getFieldVL(sfSignature);
211 }
212 
213 Blob
215 {
216  STObject st(sfGeneric);
218  st.set(sit);
219  return st.getFieldVL(sfMasterSignature);
220 }
221 
222 boost::optional<ValidatorToken>
224 {
225  try
226  {
227  std::string tokenStr;
228 
229  tokenStr.reserve(std::accumulate(
230  blob.cbegin(),
231  blob.cend(),
232  std::size_t(0),
233  [](std::size_t init, std::string const& s) {
234  return init + s.size();
235  }));
236 
237  for (auto const& line : blob)
238  tokenStr += boost::algorithm::trim_copy(line);
239 
240  tokenStr = base64_decode(tokenStr);
241 
242  Json::Reader r;
243  Json::Value token;
244 
245  if (r.parse(tokenStr, token))
246  {
247  auto const m = token.get("manifest", Json::Value{});
248  auto const k = token.get("validation_secret_key", Json::Value{});
249 
250  if (m.isString() && k.isString())
251  {
252  auto const key = strUnHex(k.asString());
253 
254  if (key && key->size() == 32)
255  return ValidatorToken{m.asString(), makeSlice(*key)};
256  }
257  }
258 
259  return boost::none;
260  }
261  catch (std::exception const&)
262  {
263  return boost::none;
264  }
265 }
266 
267 PublicKey
269 {
271  auto const iter = map_.find(pk);
272 
273  if (iter != map_.end() && !iter->second.revoked())
274  return iter->second.signingKey;
275 
276  return pk;
277 }
278 
279 PublicKey
281 {
283  auto const iter = signingToMasterKeys_.find(pk);
284 
285  if (iter != signingToMasterKeys_.end())
286  return iter->second;
287 
288  return pk;
289 }
290 
291 boost::optional<std::uint32_t>
293 {
295  auto const iter = map_.find(pk);
296 
297  if (iter != map_.end() && !iter->second.revoked())
298  return iter->second.sequence;
299 
300  return boost::none;
301 }
302 
303 boost::optional<std::string>
305 {
307  auto const iter = map_.find(pk);
308 
309  if (iter != map_.end() && !iter->second.revoked())
310  return iter->second.domain;
311 
312  return boost::none;
313 }
314 
315 boost::optional<std::string>
317 {
319  auto const iter = map_.find(pk);
320 
321  if (iter != map_.end() && !iter->second.revoked())
322  return iter->second.serialized;
323 
324  return boost::none;
325 }
326 
327 bool
329 {
331  auto const iter = map_.find(pk);
332 
333  if (iter != map_.end())
334  return iter->second.revoked();
335 
336  return false;
337 }
338 
341 {
342  std::lock_guard applyLock{apply_mutex_};
343 
344  /*
345  before we spend time checking the signature, make sure the
346  sequence number is newer than any we have.
347  */
348  auto const iter = map_.find(m.masterKey);
349 
350  if (iter != map_.end() && m.sequence <= iter->second.sequence)
351  {
352  /*
353  A manifest was received for a validator we're tracking, but
354  its sequence number is not higher than the one already stored.
355  This will happen normally when a peer without the latest gossip
356  connects.
357  */
358  if (auto stream = j_.debug())
359  logMftAct(
360  stream,
361  "Stale",
362  m.masterKey,
363  m.sequence,
364  iter->second.sequence);
365  return ManifestDisposition::stale; // not a newer manifest, ignore
366  }
367 
368  if (!m.verify())
369  {
370  /*
371  A manifest's signature is invalid.
372  This shouldn't happen normally.
373  */
374  if (auto stream = j_.warn())
375  logMftAct(stream, "Invalid", m.masterKey, m.sequence);
377  }
378 
379  std::lock_guard readLock{read_mutex_};
380 
381  bool const revoked = m.revoked();
382 
383  if (revoked)
384  {
385  /*
386  A validator master key has been compromised, so its manifests
387  are now untrustworthy. In order to prevent us from accepting
388  a forged manifest signed by the compromised master key, store
389  this manifest, which has the highest possible sequence number
390  and therefore can't be superseded by a forged one.
391  */
392  if (auto stream = j_.warn())
393  logMftAct(stream, "Revoked", m.masterKey, m.sequence);
394  }
395 
396  if (iter == map_.end())
397  {
398  /*
399  This is the first received manifest for a trusted master key
400  (possibly our own). This only happens once per validator per
401  run.
402  */
403  if (auto stream = j_.info())
404  logMftAct(stream, "AcceptedNew", m.masterKey, m.sequence);
405 
406  if (!revoked)
408 
409  auto masterKey = m.masterKey;
410  map_.emplace(std::move(masterKey), std::move(m));
411  }
412  else
413  {
414  /*
415  An ephemeral key was revoked and superseded by a new key.
416  This is expected, but should happen infrequently.
417  */
418  if (auto stream = j_.info())
419  logMftAct(
420  stream,
421  "AcceptedUpdate",
422  m.masterKey,
423  m.sequence,
424  iter->second.sequence);
425 
426  signingToMasterKeys_.erase(iter->second.signingKey);
427 
428  if (!revoked)
430 
431  iter->second = std::move(m);
432  }
433 
434  // Something has changed. Keep track of it.
435  seq_++;
436 
438 }
439 
440 void
442 {
443  // Load manifests stored in database
444  std::string const sql = "SELECT RawData FROM " + dbTable + ";";
445  auto db = dbCon.checkoutDb();
446  soci::blob sociRawData(*db);
447  soci::statement st = (db->prepare << sql, soci::into(sociRawData));
448  st.execute();
449  while (st.fetch())
450  {
451  std::string serialized;
452  convert(sociRawData, serialized);
453  if (auto mo = deserializeManifest(serialized))
454  {
455  if (!mo->verify())
456  {
457  JLOG(j_.warn()) << "Unverifiable manifest in db";
458  continue;
459  }
460 
461  applyManifest(std::move(*mo));
462  }
463  else
464  {
465  JLOG(j_.warn()) << "Malformed manifest in database";
466  }
467  }
468 }
469 
470 bool
472  DatabaseCon& dbCon,
473  std::string const& dbTable,
474  std::string const& configManifest,
475  std::vector<std::string> const& configRevocation)
476 {
477  load(dbCon, dbTable);
478 
479  if (!configManifest.empty())
480  {
481  auto mo = deserializeManifest(base64_decode(configManifest));
482  if (!mo)
483  {
484  JLOG(j_.error()) << "Malformed validator_token in config";
485  return false;
486  }
487 
488  if (mo->revoked())
489  {
490  JLOG(j_.warn()) << "Configured manifest revokes public key";
491  }
492 
493  if (applyManifest(std::move(*mo)) == ManifestDisposition::invalid)
494  {
495  JLOG(j_.error()) << "Manifest in config was rejected";
496  return false;
497  }
498  }
499 
500  if (!configRevocation.empty())
501  {
502  std::string revocationStr;
503  revocationStr.reserve(std::accumulate(
504  configRevocation.cbegin(),
505  configRevocation.cend(),
506  std::size_t(0),
507  [](std::size_t init, std::string const& s) {
508  return init + s.size();
509  }));
510 
511  for (auto const& line : configRevocation)
512  revocationStr += boost::algorithm::trim_copy(line);
513 
514  auto mo = deserializeManifest(base64_decode(revocationStr));
515 
516  if (!mo || !mo->revoked() ||
517  applyManifest(std::move(*mo)) == ManifestDisposition::invalid)
518  {
519  JLOG(j_.error()) << "Invalid validator key revocation in config";
520  return false;
521  }
522  }
523 
524  return true;
525 }
526 
527 void
529  DatabaseCon& dbCon,
530  std::string const& dbTable,
531  std::function<bool(PublicKey const&)> isTrusted)
532 {
534 
535  auto db = dbCon.checkoutDb();
536 
537  soci::transaction tr(*db);
538  *db << "DELETE FROM " << dbTable;
539  std::string const sql =
540  "INSERT INTO " + dbTable + " (RawData) VALUES (:rawData);";
541  for (auto const& v : map_)
542  {
543  // Save all revocation manifests,
544  // but only save trusted non-revocation manifests.
545  if (!v.second.revoked() && !isTrusted(v.second.masterKey))
546  {
547  JLOG(j_.info()) << "Untrusted manifest in cache not saved to db";
548  continue;
549  }
550 
551  // soci does not support bulk insertion of blob data
552  // Do not reuse blob because manifest ecdsa signatures vary in length
553  // but blob write length is expected to be >= the last write
554  soci::blob rawData(*db);
555  convert(v.second.serialized, rawData);
556  *db << sql, soci::use(rawData);
557  }
558  tr.commit();
559 }
560 } // namespace ripple
ripple::Slice::size
std::size_t size() const noexcept
Returns the number of bytes in the storage.
Definition: Slice.h:79
ripple::ManifestCache::save
void save(DatabaseCon &dbCon, std::string const &dbTable, std::function< bool(PublicKey const &)> isTrusted)
Save cached manifests to database.
Definition: app/misc/impl/Manifest.cpp:528
ripple::Manifest::domain
std::string domain
The domain, if one was specified in the manifest; empty otherwise.
Definition: Manifest.h:93
ripple::makeSlice
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:240
std::string
STL class.
ripple::ManifestDisposition
ManifestDisposition
Definition: Manifest.h:179
ripple::ManifestCache::getMasterKey
PublicKey getMasterKey(PublicKey const &pk) const
Returns ephemeral signing key's master public key.
Definition: app/misc/impl/Manifest.cpp:280
std::exception
STL class.
ripple::sfGeneric
const SField sfGeneric(access, 0)
Definition: SField.h:332
ripple::publicKeyType
boost::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
Definition: PublicKey.cpp:203
ripple::Manifest
Definition: Manifest.h:78
ripple::Slice
An immutable linear range of bytes.
Definition: Slice.h:44
std::string::reserve
T reserve(T... args)
ripple::sfSequence
const SF_UINT32 sfSequence
ripple::HashPrefix::manifest
@ manifest
Manifest.
ripple::convert
void convert(soci::blob &from, std::vector< std::uint8_t > &to)
Definition: SociDB.cpp:156
Json::Value::get
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:834
ripple::Manifest::signingKey
PublicKey signingKey
The ephemeral key associated with this manifest.
Definition: Manifest.h:87
std::vector< unsigned char >
ripple::Manifest::serialized
std::string serialized
The manifest in serialized form.
Definition: Manifest.h:81
std::string::size
T size(T... args)
ripple::Manifest::masterKey
PublicKey masterKey
The master key associated with this manifest.
Definition: Manifest.h:84
ripple::Manifest::verify
bool verify() const
Returns true if manifest signature is valid.
Definition: app/misc/impl/Manifest.cpp:168
ripple::sfSigningPubKey
const SF_VL sfSigningPubKey
ripple::toBase58
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition: AccountID.cpp:29
ripple::Slice::data
std::uint8_t const * data() const noexcept
Return a pointer to beginning of the storage.
Definition: Slice.h:96
ripple::ValidatorToken
Definition: Manifest.h:170
beast::Journal::warn
Stream warn() const
Definition: Journal.h:327
std::lock_guard
STL class.
ripple::ManifestCache::map_
hash_map< PublicKey, Manifest > map_
Active manifests stored by master public key.
Definition: Manifest.h:217
ripple::soeREQUIRED
@ soeREQUIRED
Definition: SOTemplate.h:35
ripple::strUnHex
boost::optional< Blob > strUnHex(std::size_t strSize, Iterator begin, Iterator end)
Definition: StringUtilities.h:49
std::function
ripple::STObject::getFieldVL
Blob getFieldVL(SField const &field) const
Definition: STObject.cpp:568
Json::Reader
Unserialize a JSON document into a Value.
Definition: json_reader.h:36
ripple::Slice::empty
bool empty() const noexcept
Return true if the byte range is empty.
Definition: Slice.h:68
ripple::ManifestCache::apply_mutex_
std::mutex apply_mutex_
Definition: Manifest.h:213
ripple::Manifest::revoked
bool revoked() const
Returns true if manifest revokes master key.
Definition: app/misc/impl/Manifest.cpp:193
ripple::ManifestCache::read_mutex_
std::mutex read_mutex_
Definition: Manifest.h:214
ripple::sfVersion
const SF_UINT16 sfVersion
ripple::verify
bool verify(PublicKey const &publicKey, Slice const &m, Slice const &sig, bool mustBeFullyCanonical)
Verify a signature on a message.
Definition: PublicKey.cpp:268
stdexcept
ripple::base_uint
Integers of any length that is a multiple of 32-bits.
Definition: base_uint.h:73
ripple::Manifest::getSignature
boost::optional< Blob > getSignature() const
Returns manifest signature.
Definition: app/misc/impl/Manifest.cpp:203
ripple::SOTemplate
Defines the fields and their attributes within a STObject.
Definition: SOTemplate.h:82
ripple::DatabaseCon::checkoutDb
LockedSociSession checkoutDb()
Definition: DatabaseCon.h:178
ripple::ManifestCache::signingToMasterKeys_
hash_map< PublicKey, PublicKey > signingToMasterKeys_
Master public keys stored by current ephemeral public key.
Definition: Manifest.h:220
ripple::PublicKey
A public key.
Definition: PublicKey.h:59
ripple::sfMasterSignature
const SF_VL sfMasterSignature
ripple::soeOPTIONAL
@ soeOPTIONAL
Definition: SOTemplate.h:36
ripple::ManifestCache::revoked
bool revoked(PublicKey const &pk) const
Returns true if master key has been revoked in a manifest.
Definition: app/misc/impl/Manifest.cpp:328
ripple::ManifestCache::getManifest
boost::optional< std::string > getManifest(PublicKey const &pk) const
Returns mainfest corresponding to a given public key.
Definition: app/misc/impl/Manifest.cpp:316
ripple::base64_decode
std::string base64_decode(std::string const &data)
Definition: base64.cpp:245
beast::Journal::error
Stream error() const
Definition: Journal.h:333
beast::Journal::info
Stream info() const
Definition: Journal.h:321
ripple::ManifestCache::j_
beast::Journal j_
Definition: Manifest.h:212
ripple::ManifestCache::seq_
std::atomic< std::uint32_t > seq_
Definition: Manifest.h:222
std::accumulate
T accumulate(T... args)
ripple::SerialIter
Definition: Serializer.h:308
ripple::ManifestCache::getSigningKey
PublicKey getSigningKey(PublicKey const &pk) const
Returns master key's current signing key.
Definition: app/misc/impl/Manifest.cpp:268
std::uint32_t
ripple::Manifest::getMasterSignature
Blob getMasterSignature() const
Returns manifest master key signature.
Definition: app/misc/impl/Manifest.cpp:214
ripple::ManifestDisposition::invalid
@ invalid
Timely, but invalid signature.
ripple::ManifestDisposition::accepted
@ accepted
Manifest is valid.
ripple::Manifest::sequence
std::uint32_t sequence
The sequence number of this manifest.
Definition: Manifest.h:90
ripple::STObject
Definition: STObject.h:51
ripple::ManifestDisposition::stale
@ stale
Sequence is too old.
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::deserializeManifest
boost::optional< Manifest > deserializeManifest(Slice s)
Constructs Manifest from serialized string.
Definition: app/misc/impl/Manifest.cpp:38
ripple::loadValidatorToken
boost::optional< ValidatorToken > loadValidatorToken(std::vector< std::string > const &blob)
Definition: app/misc/impl/Manifest.cpp:223
std::vector::cbegin
T cbegin(T... args)
ripple::logMftAct
Stream & logMftAct(Stream &s, std::string const &action, PublicKey const &pk, std::uint32_t seq)
Definition: app/misc/impl/Manifest.cpp:140
Json::Reader::parse
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
Definition: json_reader.cpp:74
ripple::DatabaseCon
Definition: DatabaseCon.h:81
ripple::sfSignature
const SF_VL sfSignature
ripple::Manifest::hash
uint256 hash() const
Returns hash of serialized manifest data.
Definition: app/misc/impl/Manifest.cpp:184
std::string::empty
T empty(T... args)
std::string::assign
T assign(T... args)
ripple::TokenType::NodePublic
@ NodePublic
beast::Journal::debug
Stream debug() const
Definition: Journal.h:315
std::size_t
ripple::isProperlyFormedTomlDomain
bool isProperlyFormedTomlDomain(std::string const &domain)
Determines if the given string looks like a TOML-file hosting domain.
std::vector::cend
T cend(T... args)
ripple::ManifestCache::getSequence
boost::optional< std::uint32_t > getSequence(PublicKey const &pk) const
Returns master key's current manifest sequence.
Definition: app/misc/impl/Manifest.cpp:292
numeric
std::numeric_limits::max
T max(T... args)
ripple::ManifestCache::load
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: app/misc/impl/Manifest.cpp:471
ripple::sfDomain
const SF_VL sfDomain
ripple::ManifestCache::applyManifest
ManifestDisposition applyManifest(Manifest m)
Add manifest to cache.
Definition: app/misc/impl/Manifest.cpp:340
ripple::STObject::set
void set(const SOTemplate &)
Definition: STObject.cpp:73
ripple::ManifestCache::getDomain
boost::optional< std::string > getDomain(PublicKey const &pk) const
Returns domain claimed by a given public key.
Definition: app/misc/impl/Manifest.cpp:304
std::string::data
T data(T... args)
ripple::sfPublicKey
const SF_VL sfPublicKey
ripple::soeDEFAULT
@ soeDEFAULT
Definition: SOTemplate.h:37
Json::Value
Represents a JSON value.
Definition: json_value.h:145
ripple::get
T & get(EitherAmount &amt)
Definition: AmountSpec.h:116
ripple::STObject::getHash
uint256 getHash(HashPrefix prefix) const
Definition: STObject.cpp:312