diff --git a/src/ripple/app/tests/ValidatorList_test.cpp b/src/ripple/app/tests/ValidatorList_test.cpp index 14402850f..7bd6a6948 100644 --- a/src/ripple/app/tests/ValidatorList_test.cpp +++ b/src/ripple/app/tests/ValidatorList_test.cpp @@ -38,6 +38,15 @@ private: randomSecretKey()); } + static + PublicKey + randomMasterKey () + { + return derivePublicKey ( + KeyType::ed25519, + randomSecretKey()); + } + static bool isPresent ( @@ -60,6 +69,7 @@ private: auto validators = std::make_unique (beast::Journal ()); std::vector network; + network.reserve(8); while (network.size () != 8) network.push_back (randomNode()); @@ -86,13 +96,13 @@ private: // Correct configuration s1.append (format (network[0])); - s1.append (format (network[1]) + " Comment"); - s1.append (format (network[2]) + " Multi Word Comment"); - s1.append (format (network[3]) + " Leading Whitespace"); - s1.append (format (network[4]) + " Trailing Whitespace "); - s1.append (format (network[5]) + " Leading & Trailing Whitespace "); - s1.append (format (network[6]) + " Leading, Trailing & Internal Whitespace "); - s1.append (format (network[7]) + " "); + s1.append (format (network[1], " Comment")); + s1.append (format (network[2], " Multi Word Comment")); + s1.append (format (network[3], " Leading Whitespace")); + s1.append (format (network[4], " Trailing Whitespace ")); + s1.append (format (network[5], " Leading & Trailing Whitespace ")); + s1.append (format (network[6], " Leading, Trailing & Internal Whitespace ")); + s1.append (format (network[7], " ")); expect (validators->load (s1)); @@ -123,6 +133,17 @@ private: expect (!validators->load (s5)); expect (!validators->trusted (node1)); expect (!validators->trusted (node2)); + + // Add Ed25519 master public keys to permanent validators list + auto const masterNode1 = randomMasterKey (); + auto const masterNode2 = randomMasterKey (); + + Section s6; + s6.append (format (masterNode1)); + s6.append (format (masterNode2, " Comment")); + expect (validators->load (s6)); + expect (validators->trusted (masterNode1)); + expect (validators->trusted (masterNode2)); } void diff --git a/src/ripple/overlay/impl/Manifest.cpp b/src/ripple/overlay/impl/Manifest.cpp index a52f3c981..4ebd66978 100644 --- a/src/ripple/overlay/impl/Manifest.cpp +++ b/src/ripple/overlay/impl/Manifest.cpp @@ -206,6 +206,12 @@ ManifestCache::configManifest ( } } +bool +ManifestCache::trusted (PublicKey const& identity) const +{ + return map_.count(identity); +} + void ManifestCache::addTrustedKey (PublicKey const& pk, std::string comment) { @@ -222,6 +228,13 @@ ManifestCache::addTrustedKey (PublicKey const& pk, std::string comment) value.comment = std::move(comment); } +std::size_t +ManifestCache::size () const +{ + std::lock_guard lock (mutex_); + return map_.size (); +} + ManifestDisposition ManifestCache::canApply (PublicKey const& pk, std::uint32_t seq, beast::Journal journal) const @@ -263,6 +276,16 @@ ManifestDisposition ManifestCache::applyManifest ( Manifest m, ValidatorList& unl, beast::Journal journal) { + /* + Move master public key from permanent trusted key list + to manifest cache. + */ + if (auto unlComment = unl.member (m.masterKey)) + { + addTrustedKey (m.masterKey, *unlComment); + unl.removePermanentKey (m.masterKey); + } + { std::lock_guard lock (mutex_); @@ -388,6 +411,11 @@ void ManifestCache::load ( { Throw ("Unverifiable manifest in db"); } + + // Remove master public key from permanent trusted key list + if (unl.trusted(mo->masterKey)) + unl.removePermanentKey (mo->masterKey); + // add trusted key map_[mo->masterKey]; diff --git a/src/ripple/overlay/impl/Manifest.h b/src/ripple/overlay/impl/Manifest.h index 7335ff861..fe064f2c7 100644 --- a/src/ripple/overlay/impl/Manifest.h +++ b/src/ripple/overlay/impl/Manifest.h @@ -168,8 +168,17 @@ public: void configManifest (Manifest m, ValidatorList& unl, beast::Journal journal); + /** Determines whether a node is in the trusted master key list */ + bool + trusted ( + PublicKey const& identity) const; + void addTrustedKey (PublicKey const& pk, std::string comment); + /** The number of installed trusted master keys */ + std::size_t + size () const; + ManifestDisposition applyManifest ( Manifest m, ValidatorList& unl, beast::Journal journal); diff --git a/src/ripple/overlay/impl/OverlayImpl.cpp b/src/ripple/overlay/impl/OverlayImpl.cpp index 1ff8628eb..db37e3b9f 100644 --- a/src/ripple/overlay/impl/OverlayImpl.cpp +++ b/src/ripple/overlay/impl/OverlayImpl.cpp @@ -438,7 +438,8 @@ OverlayImpl::setupValidatorKeyManifests (BasicConfig const& config, journal_); if (!loaded) - Throw ("Unable to load validator keys"); + Throw ( + "Unable to load keys from [validator_keys]"); auto const validation_manifest = config.section ("validation_manifest"); diff --git a/src/ripple/overlay/tests/manifest_test.cpp b/src/ripple/overlay/tests/manifest_test.cpp index bbd429987..0696c4e5d 100644 --- a/src/ripple/overlay/tests/manifest_test.cpp +++ b/src/ripple/overlay/tests/manifest_test.cpp @@ -36,6 +36,20 @@ namespace tests { class manifest_test : public ripple::TestSuite { private: + static PublicKey randomNode () + { + return derivePublicKey ( + KeyType::secp256k1, + randomSecretKey()); + } + + static PublicKey randomMasterKey () + { + return derivePublicKey ( + KeyType::ed25519, + randomSecretKey()); + } + static void cleanupDatabaseDir (boost::filesystem::path const& dbPath) { using namespace boost::filesystem; @@ -122,7 +136,95 @@ public: return Manifest (m.serialized, m.masterKey, m.signingKey, m.sequence); } - void testLoadStore (ManifestCache const& m, ValidatorList& unl) + + void + testConfigLoad () + { + testcase ("Config Load"); + + ManifestCache cache; + beast::Journal journal; + + std::vector network; + network.reserve(8); + + while (network.size () != 8) + network.push_back (randomMasterKey()); + + auto format = []( + PublicKey const &publicKey, + char const* comment = nullptr) + { + auto ret = toBase58( + TokenType::TOKEN_NODE_PUBLIC, + publicKey); + + if (comment) + ret += comment; + + return ret; + }; + + Section s1; + + // Correct (empty) configuration + expect (cache.loadValidatorKeys (s1, journal)); + expect (cache.size() == 0); + + // Correct configuration + s1.append (format (network[0])); + s1.append (format (network[1], " Comment")); + s1.append (format (network[2], " Multi Word Comment")); + s1.append (format (network[3], " Leading Whitespace")); + s1.append (format (network[4], " Trailing Whitespace ")); + s1.append (format (network[5], " Leading & Trailing Whitespace ")); + s1.append (format (network[6], " Leading, Trailing & Internal Whitespace ")); + s1.append (format (network[7], " ")); + + expect (cache.loadValidatorKeys (s1, journal)); + + for (auto const& n : network) + expect (cache.trusted (n)); + + // Incorrect configurations: + Section s2; + s2.append ("NotAPublicKey"); + expect (!cache.loadValidatorKeys (s2, journal)); + + Section s3; + s3.append (format (network[0], "!")); + expect (!cache.loadValidatorKeys (s3, journal)); + + Section s4; + s4.append (format (network[0], "! Comment")); + expect (!cache.loadValidatorKeys (s4, journal)); + + // Check if we properly terminate when we encounter + // a malformed or unparseable entry: + auto const masterKey1 = randomMasterKey(); + auto const masterKey2 = randomMasterKey (); + + Section s5; + s5.append (format (masterKey1, "XXX")); + s5.append (format (masterKey2)); + expect (!cache.loadValidatorKeys (s5, journal)); + expect (!cache.trusted (masterKey1)); + expect (!cache.trusted (masterKey2)); + + // Reject secp256k1 permanent validator keys + auto const node1 = randomNode (); + auto const node2 = randomNode (); + + Section s6; + s6.append (format (node1)); + s6.append (format (node2, " Comment")); + expect (!cache.loadValidatorKeys (s6, journal)); + expect (!cache.trusted (node1)); + expect (!cache.trusted (node2)); + } + + void testLoadStore (ManifestCache const& m, ValidatorList& unl, + PublicKey& pk) { testcase ("load/store"); @@ -138,7 +240,14 @@ public: ManifestCache loaded; beast::Journal journal; + + // load should remove master key from permanent key list + expect (m.trusted(pk)); + expect (unl.insertPermanentKey(pk, "trusted key")); + expect (unl.trusted(pk)); loaded.load (dbCon, unl, journal); + expect (!unl.trusted(pk)); + expect (loaded.trusted(pk)); auto getPopulatedManifests = [](ManifestCache const& cache) -> std::vector @@ -206,6 +315,7 @@ public: ManifestCache cache; beast::Journal journal; auto unl = std::make_unique (journal); + PublicKey pk; { testcase ("apply"); auto const accepted = ManifestDisposition::accepted; @@ -246,8 +356,22 @@ public: expect (!ripple::make_Manifest(fake)); expect (cache.applyManifest (clone (s_b2), *unl, journal) == invalid); + + // When trusted permanent key is found as manifest master key + // move to manifest cache + auto const sk_c = randomSecretKey(); + pk = derivePublicKey(KeyType::ed25519, sk_c); + auto const kp_c = randomKeyPair(KeyType::secp256k1); + auto const s_c0 = make_Manifest (KeyType::ed25519, sk_c, kp_c.first, 0); + expect (unl->insertPermanentKey(pk, "trusted key")); + expect (unl->trusted(pk)); + expect (!cache.trusted(pk)); + expect (cache.applyManifest(clone (s_c0), *unl, journal) == accepted); + expect (!unl->trusted(pk)); + expect (cache.trusted(pk)); } - testLoadStore (cache, *unl); + testConfigLoad(); + testLoadStore (cache, *unl, pk); testGetSignature (); } };