mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
723 lines
28 KiB
C++
723 lines
28 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
This file is part of rippled: https://github.com/ripple/rippled
|
|
Copyright 2014 Ripple Labs Inc.
|
|
|
|
Permission to use, copy, modify, and/or distribute this software for any
|
|
purpose with or without fee is hereby granted, provided that the above
|
|
copyright notice and this permission notice appear in all copies.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
//==============================================================================
|
|
|
|
#include <ripple/app/misc/Manifest.h>
|
|
#include <ripple/app/misc/ValidatorList.h>
|
|
#include <ripple/basics/base64.h>
|
|
#include <ripple/basics/contract.h>
|
|
#include <ripple/basics/StringUtilities.h>
|
|
#include <test/jtx.h>
|
|
#include <ripple/core/DatabaseCon.h>
|
|
#include <ripple/app/main/DBInit.h>
|
|
#include <ripple/protocol/SecretKey.h>
|
|
#include <ripple/protocol/Sign.h>
|
|
#include <ripple/protocol/STExchange.h>
|
|
#include <boost/filesystem.hpp>
|
|
#include <boost/algorithm/string.hpp>
|
|
#include <boost/utility/in_place_factory.hpp>
|
|
|
|
namespace ripple {
|
|
namespace test {
|
|
|
|
class Manifest_test : public beast::unit_test::suite
|
|
{
|
|
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;
|
|
if (!exists (dbPath) || !is_directory (dbPath) || !is_empty (dbPath))
|
|
return;
|
|
remove (dbPath);
|
|
}
|
|
|
|
static void setupDatabaseDir (boost::filesystem::path const& dbPath)
|
|
{
|
|
using namespace boost::filesystem;
|
|
if (!exists (dbPath))
|
|
{
|
|
create_directory (dbPath);
|
|
return;
|
|
}
|
|
|
|
if (!is_directory (dbPath))
|
|
{
|
|
// someone created a file where we want to put our directory
|
|
Throw<std::runtime_error> ("Cannot create directory: " +
|
|
dbPath.string ());
|
|
}
|
|
}
|
|
static boost::filesystem::path getDatabasePath ()
|
|
{
|
|
return boost::filesystem::current_path () / "manifest_test_databases";
|
|
}
|
|
|
|
public:
|
|
Manifest_test ()
|
|
{
|
|
try
|
|
{
|
|
setupDatabaseDir (getDatabasePath ());
|
|
}
|
|
catch (std::exception const&)
|
|
{
|
|
}
|
|
}
|
|
~Manifest_test ()
|
|
{
|
|
try
|
|
{
|
|
cleanupDatabaseDir (getDatabasePath ());
|
|
}
|
|
catch (std::exception const&)
|
|
{
|
|
}
|
|
}
|
|
|
|
std::string
|
|
makeManifestString (
|
|
PublicKey const& pk,
|
|
SecretKey const& sk,
|
|
PublicKey const& spk,
|
|
SecretKey const& ssk,
|
|
int seq)
|
|
{
|
|
STObject st(sfGeneric);
|
|
st[sfSequence] = seq;
|
|
st[sfPublicKey] = pk;
|
|
st[sfSigningPubKey] = spk;
|
|
|
|
sign(st, HashPrefix::manifest, *publicKeyType(spk), ssk);
|
|
sign(st, HashPrefix::manifest, *publicKeyType(pk), sk,
|
|
sfMasterSignature);
|
|
|
|
Serializer s;
|
|
st.add(s);
|
|
|
|
return base64_encode (std::string(
|
|
static_cast<char const*> (s.data()), s.size()));
|
|
}
|
|
|
|
Manifest
|
|
make_Manifest
|
|
(SecretKey const& sk, KeyType type, SecretKey const& ssk, KeyType stype,
|
|
int seq, bool invalidSig = false)
|
|
{
|
|
auto const pk = derivePublicKey(type, sk);
|
|
auto const spk = derivePublicKey(stype, ssk);
|
|
|
|
STObject st(sfGeneric);
|
|
st[sfSequence] = seq;
|
|
st[sfPublicKey] = pk;
|
|
st[sfSigningPubKey] = spk;
|
|
|
|
sign(st, HashPrefix::manifest, stype, ssk);
|
|
BEAST_EXPECT(verify(st, HashPrefix::manifest, spk));
|
|
|
|
sign(st, HashPrefix::manifest, type,
|
|
invalidSig ? randomSecretKey() : sk, sfMasterSignature);
|
|
BEAST_EXPECT(invalidSig ^ verify(
|
|
st, HashPrefix::manifest, pk, sfMasterSignature));
|
|
|
|
Serializer s;
|
|
st.add(s);
|
|
|
|
std::string const m (static_cast<char const*> (s.data()), s.size());
|
|
if (auto r = Manifest::make_Manifest (std::move (m)))
|
|
return std::move (*r);
|
|
Throw<std::runtime_error> ("Could not create a manifest");
|
|
return *Manifest::make_Manifest(std::move(m)); // Silence compiler warning.
|
|
}
|
|
|
|
std::string
|
|
makeRevocation
|
|
(SecretKey const& sk, KeyType type, bool invalidSig = false)
|
|
{
|
|
auto const pk = derivePublicKey(type, sk);
|
|
|
|
STObject st(sfGeneric);
|
|
st[sfSequence] = std::numeric_limits<std::uint32_t>::max ();
|
|
st[sfPublicKey] = pk;
|
|
|
|
sign(st, HashPrefix::manifest, type,
|
|
invalidSig ? randomSecretKey() : sk, sfMasterSignature);
|
|
BEAST_EXPECT(invalidSig ^ verify(
|
|
st, HashPrefix::manifest, pk, sfMasterSignature));
|
|
|
|
Serializer s;
|
|
st.add(s);
|
|
|
|
return base64_encode (std::string(
|
|
static_cast<char const*> (s.data()), s.size()));
|
|
}
|
|
|
|
Manifest
|
|
clone (Manifest const& m)
|
|
{
|
|
return Manifest (m.serialized, m.masterKey, m.signingKey, m.sequence);
|
|
}
|
|
|
|
void testLoadStore (ManifestCache& m)
|
|
{
|
|
testcase ("load/store");
|
|
|
|
std::string const dbName("ManifestCacheTestDB");
|
|
{
|
|
DatabaseCon::Setup setup;
|
|
setup.dataDir = getDatabasePath ();
|
|
DatabaseCon dbCon(setup, dbName, WalletDBInit, WalletDBCount);
|
|
|
|
auto getPopulatedManifests =
|
|
[](ManifestCache const& cache) -> std::vector<Manifest const*>
|
|
{
|
|
std::vector<Manifest const*> result;
|
|
result.reserve (32);
|
|
cache.for_each_manifest (
|
|
[&result](Manifest const& m)
|
|
{result.push_back (&m);});
|
|
return result;
|
|
};
|
|
auto sort =
|
|
[](std::vector<Manifest const*> mv) -> std::vector<Manifest const*>
|
|
{
|
|
std::sort (mv.begin (),
|
|
mv.end (),
|
|
[](Manifest const* lhs, Manifest const* rhs)
|
|
{return lhs->serialized < rhs->serialized;});
|
|
return mv;
|
|
};
|
|
std::vector<Manifest const*> const inManifests (
|
|
sort (getPopulatedManifests (m)));
|
|
|
|
beast::Journal journal;
|
|
jtx::Env env (*this);
|
|
auto unl = std::make_unique<ValidatorList> (
|
|
m, m, env.timeKeeper(), journal);
|
|
|
|
{
|
|
// save should not store untrusted master keys to db
|
|
// except for revocations
|
|
m.save (dbCon, "ValidatorManifests",
|
|
[&unl](PublicKey const& pubKey)
|
|
{
|
|
return unl->listed (pubKey);
|
|
});
|
|
|
|
ManifestCache loaded;
|
|
|
|
loaded.load (dbCon, "ValidatorManifests");
|
|
|
|
// check that all loaded manifests are revocations
|
|
std::vector<Manifest const*> const loadedManifests (
|
|
sort (getPopulatedManifests (loaded)));
|
|
|
|
for (auto const& man : loadedManifests)
|
|
BEAST_EXPECT(man->revoked());
|
|
}
|
|
{
|
|
// save should store all trusted master keys to db
|
|
PublicKey emptyLocalKey;
|
|
std::vector<std::string> s1;
|
|
std::vector<std::string> keys;
|
|
std::string cfgManifest;
|
|
for (auto const& man : inManifests)
|
|
s1.push_back (toBase58(
|
|
TokenType::NodePublic, man->masterKey));
|
|
unl->load (emptyLocalKey, s1, keys);
|
|
|
|
m.save (dbCon, "ValidatorManifests",
|
|
[&unl](PublicKey const& pubKey)
|
|
{
|
|
return unl->listed (pubKey);
|
|
});
|
|
ManifestCache loaded;
|
|
loaded.load (dbCon, "ValidatorManifests");
|
|
|
|
// check that the manifest caches are the same
|
|
std::vector<Manifest const*> const loadedManifests (
|
|
sort (getPopulatedManifests (loaded)));
|
|
|
|
if (inManifests.size () == loadedManifests.size ())
|
|
{
|
|
BEAST_EXPECT(std::equal
|
|
(inManifests.begin (), inManifests.end (),
|
|
loadedManifests.begin (),
|
|
[](Manifest const* lhs, Manifest const* rhs)
|
|
{return *lhs == *rhs;}));
|
|
}
|
|
else
|
|
{
|
|
fail ();
|
|
}
|
|
}
|
|
{
|
|
// load config manifest
|
|
ManifestCache loaded;
|
|
std::vector<std::string> const emptyRevocation;
|
|
|
|
std::string const badManifest = "bad manifest";
|
|
BEAST_EXPECT(! loaded.load (
|
|
dbCon, "ValidatorManifests", badManifest, emptyRevocation));
|
|
|
|
auto const sk = randomSecretKey();
|
|
auto const pk = derivePublicKey(KeyType::ed25519, sk);
|
|
auto const kp = randomKeyPair(KeyType::secp256k1);
|
|
|
|
std::string const cfgManifest =
|
|
makeManifestString (pk, sk, kp.first, kp.second, 0);
|
|
|
|
BEAST_EXPECT(loaded.load (
|
|
dbCon, "ValidatorManifests", cfgManifest, emptyRevocation));
|
|
}
|
|
{
|
|
// load config revocation
|
|
ManifestCache loaded;
|
|
std::string const emptyManifest;
|
|
|
|
std::vector<std::string> const badRevocation = { "bad revocation" };
|
|
BEAST_EXPECT(! loaded.load (
|
|
dbCon, "ValidatorManifests", emptyManifest, badRevocation));
|
|
|
|
auto const sk = randomSecretKey();
|
|
auto const keyType = KeyType::ed25519;
|
|
auto const pk = derivePublicKey(keyType, sk);
|
|
auto const kp = randomKeyPair(KeyType::secp256k1);
|
|
std::vector<std::string> const nonRevocation =
|
|
{ makeManifestString (pk, sk, kp.first, kp.second, 0) };
|
|
|
|
BEAST_EXPECT(! loaded.load (
|
|
dbCon, "ValidatorManifests", emptyManifest, nonRevocation));
|
|
BEAST_EXPECT(! loaded.revoked(pk));
|
|
|
|
std::vector<std::string> const badSigRevocation =
|
|
{ makeRevocation (sk, keyType, true /* invalidSig */) };
|
|
BEAST_EXPECT(! loaded.load (
|
|
dbCon, "ValidatorManifests", emptyManifest, badSigRevocation));
|
|
BEAST_EXPECT(! loaded.revoked(pk));
|
|
|
|
std::vector<std::string> const cfgRevocation =
|
|
{ makeRevocation (sk, keyType) };
|
|
BEAST_EXPECT(loaded.load (
|
|
dbCon, "ValidatorManifests", emptyManifest, cfgRevocation));
|
|
|
|
BEAST_EXPECT(loaded.revoked(pk));
|
|
}
|
|
}
|
|
boost::filesystem::remove (getDatabasePath () /
|
|
boost::filesystem::path (dbName));
|
|
}
|
|
|
|
void testGetSignature()
|
|
{
|
|
testcase ("getSignature");
|
|
auto const sk = randomSecretKey();
|
|
auto const pk = derivePublicKey(KeyType::ed25519, sk);
|
|
auto const kp = randomKeyPair(KeyType::secp256k1);
|
|
auto const m = make_Manifest (
|
|
sk, KeyType::ed25519, kp.second, KeyType::secp256k1, 0);
|
|
|
|
STObject st(sfGeneric);
|
|
st[sfSequence] = 0;
|
|
st[sfPublicKey] = pk;
|
|
st[sfSigningPubKey] = kp.first;
|
|
Serializer ss;
|
|
ss.add32(HashPrefix::manifest);
|
|
st.addWithoutSigningFields(ss);
|
|
auto const sig = sign(KeyType::secp256k1, kp.second, ss.slice());
|
|
BEAST_EXPECT(strHex(sig) == strHex(m.getSignature()));
|
|
|
|
auto const masterSig = sign(KeyType::ed25519, sk, ss.slice());
|
|
BEAST_EXPECT(strHex(masterSig) == strHex(m.getMasterSignature()));
|
|
}
|
|
|
|
void testGetKeys()
|
|
{
|
|
testcase ("getKeys");
|
|
|
|
ManifestCache cache;
|
|
auto const sk = randomSecretKey();
|
|
auto const pk = derivePublicKey(KeyType::ed25519, sk);
|
|
|
|
// getSigningKey should return same key if there is no manifest
|
|
BEAST_EXPECT(cache.getSigningKey(pk) == pk);
|
|
|
|
// getSigningKey should return the ephemeral public key
|
|
// for the listed validator master public key
|
|
// getMasterKey should return the listed validator master key
|
|
// for that ephemeral public key
|
|
auto const kp0 = randomKeyPair(KeyType::secp256k1);
|
|
auto const m0 = make_Manifest (
|
|
sk, KeyType::ed25519, kp0.second, KeyType::secp256k1, 0);
|
|
BEAST_EXPECT(cache.applyManifest(clone (m0)) ==
|
|
ManifestDisposition::accepted);
|
|
BEAST_EXPECT(cache.getSigningKey(pk) == kp0.first);
|
|
BEAST_EXPECT(cache.getMasterKey(kp0.first) == pk);
|
|
|
|
// getSigningKey should return the latest ephemeral public key
|
|
// for the listed validator master public key
|
|
// getMasterKey should only return a master key for the latest
|
|
// ephemeral public key
|
|
auto const kp1 = randomKeyPair(KeyType::secp256k1);
|
|
auto const m1 = make_Manifest (
|
|
sk, KeyType::ed25519, kp1.second, KeyType::secp256k1, 1);
|
|
BEAST_EXPECT(cache.applyManifest(clone (m1)) ==
|
|
ManifestDisposition::accepted);
|
|
BEAST_EXPECT(cache.getSigningKey(pk) == kp1.first);
|
|
BEAST_EXPECT(cache.getMasterKey(kp1.first) == pk);
|
|
BEAST_EXPECT(cache.getMasterKey(kp0.first) == kp0.first);
|
|
|
|
// getSigningKey and getMasterKey should return the same keys if
|
|
// a new manifest is applied with the same signing key but a higher
|
|
// sequence
|
|
auto const m2 = make_Manifest (
|
|
sk, KeyType::ed25519, kp1.second, KeyType::secp256k1, 2);
|
|
BEAST_EXPECT(cache.applyManifest(clone (m2)) ==
|
|
ManifestDisposition::accepted);
|
|
BEAST_EXPECT(cache.getSigningKey(pk) == kp1.first);
|
|
BEAST_EXPECT(cache.getMasterKey(kp1.first) == pk);
|
|
BEAST_EXPECT(cache.getMasterKey(kp0.first) == kp0.first);
|
|
|
|
// getSigningKey should return boost::none for a
|
|
// revoked master public key
|
|
// getMasterKey should return boost::none for an ephemeral public key
|
|
// from a revoked master public key
|
|
auto const kpMax = randomKeyPair(KeyType::secp256k1);
|
|
auto const mMax = make_Manifest (
|
|
sk, KeyType::ed25519, kpMax.second, KeyType::secp256k1,
|
|
std::numeric_limits<std::uint32_t>::max ());
|
|
BEAST_EXPECT(cache.applyManifest(clone (mMax)) ==
|
|
ManifestDisposition::accepted);
|
|
BEAST_EXPECT(cache.revoked(pk));
|
|
BEAST_EXPECT(cache.getSigningKey(pk) == pk);
|
|
BEAST_EXPECT(cache.getMasterKey(kpMax.first) == kpMax.first);
|
|
BEAST_EXPECT(cache.getMasterKey(kp1.first) == kp1.first);
|
|
}
|
|
|
|
void testValidatorToken()
|
|
{
|
|
testcase ("validator token");
|
|
|
|
{
|
|
auto const valSecret = parseBase58<SecretKey>(
|
|
TokenType::NodePrivate,
|
|
"paQmjZ37pKKPMrgadBLsuf9ab7Y7EUNzh27LQrZqoexpAs31nJi");
|
|
|
|
// Format token string to test trim()
|
|
std::vector<std::string> const tokenBlob = {
|
|
" eyJ2YWxpZGF0aW9uX3NlY3JldF9rZXkiOiI5ZWQ0NWY4NjYyNDFjYzE4YTI3NDdiNT\n",
|
|
" \tQzODdjMDYyNTkwNzk3MmY0ZTcxOTAyMzFmYWE5Mzc0NTdmYTlkYWY2IiwibWFuaWZl \n",
|
|
"\tc3QiOiJKQUFBQUFGeEllMUZ0d21pbXZHdEgyaUNjTUpxQzlnVkZLaWxHZncxL3ZDeE\n",
|
|
"\t hYWExwbGMyR25NaEFrRTFhZ3FYeEJ3RHdEYklENk9NU1l1TTBGREFscEFnTms4U0tG\t \t\n",
|
|
"bjdNTzJmZGtjd1JRSWhBT25ndTlzQUtxWFlvdUorbDJWMFcrc0FPa1ZCK1pSUzZQU2\n",
|
|
"hsSkFmVXNYZkFpQnNWSkdlc2FhZE9KYy9hQVpva1MxdnltR21WcmxIUEtXWDNZeXd1\n",
|
|
"NmluOEhBU1FLUHVnQkQ2N2tNYVJGR3ZtcEFUSGxHS0pkdkRGbFdQWXk1QXFEZWRGdj\n",
|
|
"VUSmEydzBpMjFlcTNNWXl3TFZKWm5GT3I3QzBrdzJBaVR6U0NqSXpkaXRROD0ifQ==\n"
|
|
};
|
|
|
|
auto const manifest =
|
|
"JAAAAAFxIe1FtwmimvGtH2iCcMJqC9gVFKilGfw1/vCxHXXLplc2GnMhAkE1agqXxBwD"
|
|
"wDbID6OMSYuM0FDAlpAgNk8SKFn7MO2fdkcwRQIhAOngu9sAKqXYouJ+l2V0W+sAOkVB"
|
|
"+ZRS6PShlJAfUsXfAiBsVJGesaadOJc/aAZokS1vymGmVrlHPKWX3Yywu6in8HASQKPu"
|
|
"gBD67kMaRFGvmpATHlGKJdvDFlWPYy5AqDedFv5TJa2w0i21eq3MYywLVJZnFOr7C0kw"
|
|
"2AiTzSCjIzditQ8=";
|
|
|
|
auto const token = ValidatorToken::make_ValidatorToken(tokenBlob);
|
|
BEAST_EXPECT(token);
|
|
BEAST_EXPECT(token->validationSecret == *valSecret);
|
|
BEAST_EXPECT(token->manifest == manifest);
|
|
}
|
|
{
|
|
std::vector<std::string> const badToken = { "bad token" };
|
|
BEAST_EXPECT(! ValidatorToken::make_ValidatorToken(badToken));
|
|
}
|
|
}
|
|
|
|
void testMakeManifest()
|
|
{
|
|
testcase ("make_Manifest");
|
|
|
|
std::array<KeyType, 2> const keyTypes {{
|
|
KeyType::ed25519,
|
|
KeyType::secp256k1 }};
|
|
|
|
std::uint32_t sequence = 0;
|
|
|
|
// public key with invalid type
|
|
auto const ret = strUnHex("9930E7FC9D56BB25D6893BA3F317AE5BCF33B3291BD63DB32654A313222F7FD020");
|
|
auto const badKey = Slice{ret.first.data(), ret.first.size()};
|
|
|
|
// short public key
|
|
auto const retShort = strUnHex("0330");
|
|
auto const shortKey = Slice{retShort.first.data(), retShort.first.size()};
|
|
|
|
auto toString = [](STObject const& st)
|
|
{
|
|
Serializer s;
|
|
st.add(s);
|
|
|
|
return std::string (static_cast<char const*> (s.data()), s.size());
|
|
};
|
|
|
|
for (auto const keyType : keyTypes)
|
|
{
|
|
auto const sk = generateSecretKey (keyType, randomSeed ());
|
|
auto const pk = derivePublicKey(keyType, sk);
|
|
|
|
for (auto const sKeyType : keyTypes)
|
|
{
|
|
auto const ssk = generateSecretKey (sKeyType, randomSeed ());
|
|
auto const spk = derivePublicKey(sKeyType, ssk);
|
|
|
|
auto buildManifestObject = [&](
|
|
std::uint32_t const& seq,
|
|
bool noSigningPublic = false,
|
|
bool noSignature = false)
|
|
{
|
|
STObject st(sfGeneric);
|
|
st[sfSequence] = seq;
|
|
st[sfPublicKey] = pk;
|
|
|
|
if (! noSigningPublic)
|
|
st[sfSigningPubKey] = spk;
|
|
|
|
sign(st, HashPrefix::manifest, keyType, sk,
|
|
sfMasterSignature);
|
|
|
|
if (! noSignature)
|
|
sign(st, HashPrefix::manifest, sKeyType, ssk);
|
|
|
|
return st;
|
|
};
|
|
|
|
auto const st = buildManifestObject(++sequence);
|
|
|
|
{
|
|
// valid manifest
|
|
auto const m = toString(st);
|
|
|
|
auto const manifest = Manifest::make_Manifest (m);
|
|
|
|
BEAST_EXPECT(manifest);
|
|
BEAST_EXPECT(manifest->masterKey == pk);
|
|
BEAST_EXPECT(manifest->signingKey == spk);
|
|
BEAST_EXPECT(manifest->sequence == sequence);
|
|
BEAST_EXPECT(manifest->serialized == m);
|
|
BEAST_EXPECT(manifest->verify());
|
|
}
|
|
{
|
|
// valid manifest with invalid signature
|
|
auto badSigSt = st;
|
|
badSigSt[sfPublicKey] = badSigSt[sfSigningPubKey];
|
|
|
|
auto const m = toString(badSigSt);
|
|
auto const manifest = Manifest::make_Manifest (m);
|
|
|
|
BEAST_EXPECT(manifest);
|
|
BEAST_EXPECT(manifest->masterKey == spk);
|
|
BEAST_EXPECT(manifest->signingKey == spk);
|
|
BEAST_EXPECT(manifest->sequence == sequence);
|
|
BEAST_EXPECT(manifest->serialized == m);
|
|
BEAST_EXPECT(! manifest->verify());
|
|
}
|
|
{
|
|
// reject missing sequence
|
|
auto badSt = st;
|
|
BEAST_EXPECT(badSt.delField(sfSequence));
|
|
BEAST_EXPECT(! Manifest::make_Manifest (toString(badSt)));
|
|
}
|
|
{
|
|
// reject missing public key
|
|
auto badSt = st;
|
|
BEAST_EXPECT(badSt.delField(sfPublicKey));
|
|
BEAST_EXPECT(! Manifest::make_Manifest (toString(badSt)));
|
|
}
|
|
{
|
|
// reject invalid public key type
|
|
auto badSt = st;
|
|
badSt[sfPublicKey] = badKey;
|
|
BEAST_EXPECT(! Manifest::make_Manifest (toString(badSt)));
|
|
}
|
|
{
|
|
// reject short public key
|
|
auto badSt = st;
|
|
badSt[sfPublicKey] = shortKey;
|
|
BEAST_EXPECT(! Manifest::make_Manifest (toString(badSt)));
|
|
}
|
|
{
|
|
// reject missing signing public key
|
|
auto badSt = st;
|
|
BEAST_EXPECT(badSt.delField(sfSigningPubKey));
|
|
BEAST_EXPECT(! Manifest::make_Manifest (toString(badSt)));
|
|
}
|
|
{
|
|
// reject invalid signing public key type
|
|
auto badSt = st;
|
|
badSt[sfSigningPubKey] = badKey;
|
|
BEAST_EXPECT(! Manifest::make_Manifest (toString(badSt)));
|
|
}
|
|
{
|
|
// reject short signing public key
|
|
auto badSt = st;
|
|
badSt[sfSigningPubKey] = shortKey;
|
|
BEAST_EXPECT(! Manifest::make_Manifest (toString(badSt)));
|
|
}
|
|
{
|
|
// reject missing signature
|
|
auto badSt = st;
|
|
BEAST_EXPECT(badSt.delField(sfMasterSignature));
|
|
BEAST_EXPECT(! Manifest::make_Manifest (toString(badSt)));
|
|
}
|
|
{
|
|
// reject missing signing key signature
|
|
auto badSt = st;
|
|
BEAST_EXPECT(badSt.delField(sfSignature));
|
|
BEAST_EXPECT(! Manifest::make_Manifest (toString(badSt)));
|
|
}
|
|
|
|
// test revocations (max sequence revoking the master key)
|
|
auto testRevocation = [&](STObject const& st)
|
|
{
|
|
auto const m = toString(st);
|
|
auto const manifest = Manifest::make_Manifest (m);
|
|
|
|
BEAST_EXPECT(manifest);
|
|
BEAST_EXPECT(manifest->masterKey == pk);
|
|
BEAST_EXPECT(manifest->signingKey == PublicKey());
|
|
BEAST_EXPECT(manifest->sequence ==
|
|
std::numeric_limits<std::uint32_t>::max ());
|
|
BEAST_EXPECT(manifest->serialized == m);
|
|
BEAST_EXPECT(manifest->verify());
|
|
};
|
|
|
|
// valid revocation
|
|
{
|
|
auto const revSt = buildManifestObject(
|
|
std::numeric_limits<std::uint32_t>::max ());
|
|
testRevocation(revSt);
|
|
}
|
|
|
|
// signing key and signature are optional in revocation
|
|
{
|
|
auto const revSt = buildManifestObject(
|
|
std::numeric_limits<std::uint32_t>::max (),
|
|
true /* no signing key */);
|
|
testRevocation(revSt);
|
|
}
|
|
{
|
|
auto const revSt = buildManifestObject(
|
|
std::numeric_limits<std::uint32_t>::max (),
|
|
false, true /* no signature */);
|
|
testRevocation(revSt);
|
|
|
|
}
|
|
{
|
|
auto const revSt = buildManifestObject(
|
|
std::numeric_limits<std::uint32_t>::max (),
|
|
true /* no signing key */,
|
|
true /* no signature */);
|
|
testRevocation(revSt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
run() override
|
|
{
|
|
ManifestCache cache;
|
|
{
|
|
testcase ("apply");
|
|
auto const accepted = ManifestDisposition::accepted;
|
|
auto const stale = ManifestDisposition::stale;
|
|
auto const invalid = ManifestDisposition::invalid;
|
|
|
|
auto const sk_a = randomSecretKey();
|
|
auto const pk_a = derivePublicKey(KeyType::ed25519, sk_a);
|
|
auto const kp_a = randomKeyPair(KeyType::secp256k1);
|
|
auto const s_a0 = make_Manifest (
|
|
sk_a, KeyType::ed25519, kp_a.second, KeyType::secp256k1, 0);
|
|
auto const s_a1 = make_Manifest (
|
|
sk_a, KeyType::ed25519, kp_a.second, KeyType::secp256k1, 1);
|
|
auto const s_aMax = make_Manifest (
|
|
sk_a, KeyType::ed25519, kp_a.second, KeyType::secp256k1,
|
|
std::numeric_limits<std::uint32_t>::max ());
|
|
|
|
auto const sk_b = randomSecretKey();
|
|
auto const kp_b = randomKeyPair(KeyType::secp256k1);
|
|
auto const s_b0 = make_Manifest (
|
|
sk_b, KeyType::ed25519, kp_b.second, KeyType::secp256k1, 0);
|
|
auto const s_b1 = make_Manifest (
|
|
sk_b, KeyType::ed25519, kp_b.second, KeyType::secp256k1, 1);
|
|
auto const s_b2 = make_Manifest (
|
|
sk_b, KeyType::ed25519, kp_b.second, KeyType::secp256k1, 2,
|
|
true); // invalidSig
|
|
auto const fake = s_b1.serialized + '\0';
|
|
|
|
// applyManifest should accept new manifests with
|
|
// higher sequence numbers
|
|
BEAST_EXPECT(cache.applyManifest (clone (s_a0)) == accepted);
|
|
BEAST_EXPECT(cache.applyManifest (clone (s_a0)) == stale);
|
|
|
|
BEAST_EXPECT(cache.applyManifest (clone (s_a1)) == accepted);
|
|
BEAST_EXPECT(cache.applyManifest (clone (s_a1)) == stale);
|
|
BEAST_EXPECT(cache.applyManifest (clone (s_a0)) == stale);
|
|
|
|
// applyManifest should accept manifests with max sequence numbers
|
|
// that revoke the master public key
|
|
BEAST_EXPECT(!cache.revoked (pk_a));
|
|
BEAST_EXPECT(s_aMax.revoked ());
|
|
BEAST_EXPECT(cache.applyManifest (clone (s_aMax)) == accepted);
|
|
BEAST_EXPECT(cache.applyManifest (clone (s_aMax)) == stale);
|
|
BEAST_EXPECT(cache.applyManifest (clone (s_a1)) == stale);
|
|
BEAST_EXPECT(cache.applyManifest (clone (s_a0)) == stale);
|
|
BEAST_EXPECT(cache.revoked (pk_a));
|
|
|
|
// applyManifest should reject manifests with invalid signatures
|
|
BEAST_EXPECT(cache.applyManifest (clone (s_b0)) == accepted);
|
|
BEAST_EXPECT(cache.applyManifest (clone (s_b0)) == stale);
|
|
|
|
BEAST_EXPECT(!Manifest::make_Manifest(fake));
|
|
BEAST_EXPECT(cache.applyManifest (clone (s_b2)) == invalid);
|
|
}
|
|
testLoadStore (cache);
|
|
testGetSignature ();
|
|
testGetKeys ();
|
|
testValidatorToken ();
|
|
testMakeManifest ();
|
|
}
|
|
};
|
|
|
|
BEAST_DEFINE_TESTSUITE(Manifest,app,ripple);
|
|
|
|
} // test
|
|
} // ripple
|