rippled
Loading...
Searching...
No Matches
Manifest_test.cpp
1#include <test/jtx.h>
2#include <test/unit_test/utils.h>
3
4#include <xrpld/app/misc/ValidatorList.h>
5
6#include <xrpl/basics/base64.h>
7#include <xrpl/basics/contract.h>
8#include <xrpl/protocol/STExchange.h>
9#include <xrpl/protocol/SecretKey.h>
10#include <xrpl/protocol/Sign.h>
11#include <xrpl/rdb/DBInit.h>
12#include <xrpl/server/Manifest.h>
13#include <xrpl/server/Wallet.h>
14
15#include <boost/algorithm/string.hpp>
16#include <boost/filesystem.hpp>
17#include <boost/utility/in_place_factory.hpp>
18
19namespace xrpl {
20namespace test {
21
23{
24private:
25 static PublicKey
30
31 static PublicKey
36
37 static void
38 cleanupDatabaseDir(boost::filesystem::path const& dbPath)
39 {
40 using namespace boost::filesystem;
41 if (!exists(dbPath) || !is_directory(dbPath) || !is_empty(dbPath))
42 return;
43 remove(dbPath);
44 }
45
46 static void
47 setupDatabaseDir(boost::filesystem::path const& dbPath)
48 {
49 using namespace boost::filesystem;
50 if (!exists(dbPath))
51 {
52 create_directory(dbPath);
53 return;
54 }
55
56 if (!is_directory(dbPath))
57 {
58 // someone created a file where we want to put our directory
59 Throw<std::runtime_error>("Cannot create directory: " + dbPath.string());
60 }
61 }
62 static boost::filesystem::path
64 {
65 return boost::filesystem::current_path() / "manifest_test_databases";
66 }
67
68public:
70 {
71 try
72 {
74 }
75 catch (std::exception const&) // NOLINT(bugprone-empty-catch)
76 {
77 }
78 }
80 {
81 try
82 {
84 }
85 catch (std::exception const&) // NOLINT(bugprone-empty-catch)
86 {
87 }
88 }
89
90 static std::string
92 PublicKey const& pk,
93 SecretKey const& sk,
94 PublicKey const& spk,
95 SecretKey const& ssk,
96 int seq)
97 {
99 st[sfSequence] = seq;
100 st[sfPublicKey] = pk;
101 st[sfSigningPubKey] = spk;
102
103 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
104 sign(st, HashPrefix::manifest, *publicKeyType(spk), ssk);
105
106 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
107 sign(st, HashPrefix::manifest, *publicKeyType(pk), sk, sfMasterSignature);
108
109 Serializer s;
110 st.add(s);
111
112 return base64_encode(std::string(static_cast<char const*>(s.data()), s.size()));
113 }
114
116 makeRevocationString(SecretKey const& sk, KeyType type, bool invalidSig = false)
117 {
118 auto const pk = derivePublicKey(type, sk);
119
122 st[sfPublicKey] = pk;
123
124 sign(
125 st, HashPrefix::manifest, type, invalidSig ? randomSecretKey() : sk, sfMasterSignature);
126 BEAST_EXPECT(invalidSig ^ verify(st, HashPrefix::manifest, pk, sfMasterSignature));
127
128 Serializer s;
129 st.add(s);
130
131 return base64_encode(std::string(static_cast<char const*>(s.data()), s.size()));
132 }
133
135 makeRevocation(SecretKey const& sk, KeyType type, bool invalidSig = false)
136 {
137 auto const pk = derivePublicKey(type, sk);
138
141 st[sfPublicKey] = pk;
142
143 sign(
144 st, HashPrefix::manifest, type, invalidSig ? randomSecretKey() : sk, sfMasterSignature);
145 BEAST_EXPECT(invalidSig ^ verify(st, HashPrefix::manifest, pk, sfMasterSignature));
146
147 Serializer s;
148 st.add(s);
149
150 std::string const m(static_cast<char const*>(s.data()), s.size());
151 if (auto r = deserializeManifest(m))
152 return std::move(*r);
153 Throw<std::runtime_error>("Could not create a revocation manifest");
154 return *deserializeManifest(std::string{}); // Silence compiler warning.
155 }
156
159 SecretKey const& sk,
160 KeyType type,
161 SecretKey const& ssk,
162 KeyType stype,
163 int seq,
164 bool invalidSig = false)
165 {
166 auto const pk = derivePublicKey(type, sk);
167 auto const spk = derivePublicKey(stype, ssk);
168
170 st[sfSequence] = seq;
171 st[sfPublicKey] = pk;
172 st[sfSigningPubKey] = spk;
173
174 sign(st, HashPrefix::manifest, stype, ssk);
175 BEAST_EXPECT(verify(st, HashPrefix::manifest, spk));
176
177 sign(
178 st, HashPrefix::manifest, type, invalidSig ? randomSecretKey() : sk, sfMasterSignature);
179 BEAST_EXPECT(invalidSig ^ verify(st, HashPrefix::manifest, pk, sfMasterSignature));
180
181 Serializer s;
182 st.add(s);
183
184 std::string const m(static_cast<char const*>(s.data()), s.size());
185 if (auto r = deserializeManifest(m))
186 return std::move(*r);
187 Throw<std::runtime_error>("Could not create a manifest");
188 return *deserializeManifest(std::string{}); // Silence compiler warning.
189 }
190
191 static Manifest
192 clone(Manifest const& m)
193 {
195 return m2;
196 }
197
198 void
200 {
201 testcase("load/store");
202
203 std::string const dbName("ManifestCacheTestDB");
204 {
205 jtx::Env env(*this);
206 DatabaseCon::Setup setup;
207 setup.dataDir = getDatabasePath();
208 assert(!setup.useGlobalPragma);
209
210 auto dbCon = makeTestWalletDB(setup, dbName, env.journal);
211
212 auto getPopulatedManifests =
213 [](ManifestCache const& cache) -> std::vector<Manifest const*> {
215 result.reserve(32);
216 cache.for_each_manifest([&result](Manifest const& man) { result.push_back(&man); });
217 return result;
218 };
220 std::sort(mv.begin(), mv.end(), [](Manifest const* lhs, Manifest const* rhs) {
221 return lhs->serialized < rhs->serialized;
222 });
223 return mv;
224 };
225 std::vector<Manifest const*> const inManifests(sort(getPopulatedManifests(m)));
226
227 auto& app = env.app();
229 m, m, env.timeKeeper(), app.config().legacy("database_path"), env.journal);
230
231 {
232 // save should not store untrusted master keys to db
233 // except for revocations
234 m.save(*dbCon, "ValidatorManifests", [&unl](PublicKey const& pubKey) {
235 return unl->listed(pubKey);
236 });
237
238 ManifestCache loaded;
239
240 loaded.load(*dbCon, "ValidatorManifests");
241
242 // check that all loaded manifests are revocations
243 std::vector<Manifest const*> const loadedManifests(
244 sort(getPopulatedManifests(loaded)));
245
246 for (auto const& man : loadedManifests)
247 BEAST_EXPECT(man->revoked());
248 }
249 {
250 // save should store all trusted master keys to db
252 std::vector<std::string> const keys;
253 s1.reserve(inManifests.size());
254
255 for (auto const& man : inManifests)
256 s1.push_back(toBase58(TokenType::NodePublic, man->masterKey));
257 unl->load({}, s1, keys);
258
259 m.save(*dbCon, "ValidatorManifests", [&unl](PublicKey const& pubKey) {
260 return unl->listed(pubKey);
261 });
262 ManifestCache loaded;
263 loaded.load(*dbCon, "ValidatorManifests");
264
265 // check that the manifest caches are the same
266 std::vector<Manifest const*> const loadedManifests(
267 sort(getPopulatedManifests(loaded)));
268
269 if (inManifests.size() == loadedManifests.size())
270 {
271 BEAST_EXPECT(
273 inManifests.begin(),
274 inManifests.end(),
275 loadedManifests.begin(),
276 [](Manifest const* lhs, Manifest const* rhs) { return *lhs == *rhs; }));
277 }
278 else
279 {
280 fail();
281 }
282 }
283 {
284 // load config manifest
285 ManifestCache loaded;
286 std::vector<std::string> const emptyRevocation;
287
288 std::string const badManifest = "bad manifest";
289 BEAST_EXPECT(
290 !loaded.load(*dbCon, "ValidatorManifests", badManifest, emptyRevocation));
291
292 auto const sk = randomSecretKey();
293 auto const pk = derivePublicKey(KeyType::ed25519, sk);
294 auto const kp = randomKeyPair(KeyType::secp256k1);
295
296 std::string const cfgManifest = makeManifestString(pk, sk, kp.first, kp.second, 0);
297
298 BEAST_EXPECT(
299 loaded.load(*dbCon, "ValidatorManifests", cfgManifest, emptyRevocation));
300 }
301 {
302 // load config revocation
303 ManifestCache loaded;
304 std::string const emptyManifest;
305
306 std::vector<std::string> const badRevocation = {"bad revocation"};
307 BEAST_EXPECT(
308 !loaded.load(*dbCon, "ValidatorManifests", emptyManifest, badRevocation));
309
310 auto const sk = randomSecretKey();
311 auto const keyType = KeyType::ed25519;
312 auto const pk = derivePublicKey(keyType, sk);
313 auto const kp = randomKeyPair(KeyType::secp256k1);
314 std::vector<std::string> const nonRevocation = {
315 makeManifestString(pk, sk, kp.first, kp.second, 0)};
316
317 BEAST_EXPECT(
318 !loaded.load(*dbCon, "ValidatorManifests", emptyManifest, nonRevocation));
319 BEAST_EXPECT(!loaded.revoked(pk));
320
321 std::vector<std::string> const badSigRevocation = {
322 makeRevocationString(sk, keyType, true)};
323 BEAST_EXPECT(
324 !loaded.load(*dbCon, "ValidatorManifests", emptyManifest, badSigRevocation));
325 BEAST_EXPECT(!loaded.revoked(pk));
326
327 std::vector<std::string> const cfgRevocation = {makeRevocationString(sk, keyType)};
328 BEAST_EXPECT(
329 loaded.load(*dbCon, "ValidatorManifests", emptyManifest, cfgRevocation));
330
331 BEAST_EXPECT(loaded.revoked(pk));
332 }
333 }
334 boost::filesystem::remove(getDatabasePath() / boost::filesystem::path(dbName));
335 }
336
337 void
339 {
340 testcase("getSignature");
341 auto const sk = randomSecretKey();
342 auto const pk = derivePublicKey(KeyType::ed25519, sk);
343 auto const kp = randomKeyPair(KeyType::secp256k1);
344 auto const m = makeManifest(sk, KeyType::ed25519, kp.second, KeyType::secp256k1, 0);
345
347 st[sfSequence] = 0;
348 st[sfPublicKey] = pk;
349 st[sfSigningPubKey] = kp.first;
350 Serializer ss;
353 auto const sig = sign(KeyType::secp256k1, kp.second, ss.slice());
354 BEAST_EXPECT(
355 strHex(sig) ==
356 strHex(*m.getSignature())); // NOLINT(bugprone-unchecked-optional-access)
357
358 auto const masterSig = sign(KeyType::ed25519, sk, ss.slice());
359 BEAST_EXPECT(strHex(masterSig) == strHex(m.getMasterSignature()));
360 }
361
362 void
364 {
365 testcase("getKeys");
366
367 ManifestCache cache;
368 auto const sk = randomSecretKey();
369 auto const pk = derivePublicKey(KeyType::ed25519, sk);
370
371 // getSigningKey should return same key if there is no manifest
372 BEAST_EXPECT(cache.getSigningKey(pk) == pk);
373
374 // getSigningKey should return the ephemeral public key
375 // for the listed validator master public key
376 // getMasterKey should return the listed validator master key
377 // for that ephemeral public key
378 auto const kp0 = randomKeyPair(KeyType::secp256k1);
379 BEAST_EXPECT(
381 cache.applyManifest(
382 makeManifest(sk, KeyType::ed25519, kp0.second, KeyType::secp256k1, 0)));
383 BEAST_EXPECT(cache.getSigningKey(pk) == kp0.first);
384 BEAST_EXPECT(cache.getMasterKey(kp0.first) == pk);
385
386 // getSigningKey should return the latest ephemeral public key
387 // for the listed validator master public key
388 // getMasterKey should only return a master key for the latest
389 // ephemeral public key
390 auto const kp1 = randomKeyPair(KeyType::secp256k1);
391 BEAST_EXPECT(
393 cache.applyManifest(
394 makeManifest(sk, KeyType::ed25519, kp1.second, KeyType::secp256k1, 1)));
395 BEAST_EXPECT(cache.getSigningKey(pk) == kp1.first);
396 BEAST_EXPECT(cache.getMasterKey(kp1.first) == pk);
397 BEAST_EXPECT(cache.getMasterKey(kp0.first) == kp0.first);
398
399 // getSigningKey and getMasterKey should fail if a new manifest is
400 // applied with the same signing key but a higher sequence
401 BEAST_EXPECT(
403 cache.applyManifest(
404 makeManifest(sk, KeyType::ed25519, kp1.second, KeyType::secp256k1, 2)));
405 BEAST_EXPECT(cache.getSigningKey(pk) == kp1.first);
406 BEAST_EXPECT(cache.getMasterKey(kp1.first) == pk);
407 BEAST_EXPECT(cache.getMasterKey(kp0.first) == kp0.first);
408
409 // getSigningKey should return std::nullopt for a revoked master public
410 // key getMasterKey should return std::nullopt for an ephemeral public
411 // key from a revoked master public key
412 BEAST_EXPECT(
415 BEAST_EXPECT(cache.revoked(pk));
416 BEAST_EXPECT(cache.getSigningKey(pk) == pk);
417 BEAST_EXPECT(cache.getMasterKey(kp0.first) == kp0.first);
418 BEAST_EXPECT(cache.getMasterKey(kp1.first) == kp1.first);
419 }
420
421 void
423 {
424 testcase("validator token");
425
426 {
427 auto const valSecret = parseBase58<SecretKey>(
428 TokenType::NodePrivate, "paQmjZ37pKKPMrgadBLsuf9ab7Y7EUNzh27LQrZqoexpAs31nJi");
429
430 // Format token string to test trim()
431 std::vector<std::string> const tokenBlob = {
432 " eyJ2YWxpZGF0aW9uX3NlY3JldF9rZXkiOiI5ZWQ0NWY4NjYyNDFjYzE4YTI3NDdiNT\n",
433 " \tQzODdjMDYyNTkwNzk3MmY0ZTcxOTAyMzFmYWE5Mzc0NTdmYTlkYWY2IiwibWFuaWZl \n",
434 "\tc3QiOiJKQUFBQUFGeEllMUZ0d21pbXZHdEgyaUNjTUpxQzlnVkZLaWxHZncxL3ZDeE\n",
435 "\t hYWExwbGMyR25NaEFrRTFhZ3FYeEJ3RHdEYklENk9NU1l1TTBGREFscEFnTms4U0tG\t \t\n",
436 "bjdNTzJmZGtjd1JRSWhBT25ndTlzQUtxWFlvdUorbDJWMFcrc0FPa1ZCK1pSUzZQU2\n",
437 "hsSkFmVXNYZkFpQnNWSkdlc2FhZE9KYy9hQVpva1MxdnltR21WcmxIUEtXWDNZeXd1\n",
438 "NmluOEhBU1FLUHVnQkQ2N2tNYVJGR3ZtcEFUSGxHS0pkdkRGbFdQWXk1QXFEZWRGdj\n",
439 "VUSmEydzBpMjFlcTNNWXl3TFZKWm5GT3I3QzBrdzJBaVR6U0NqSXpkaXRROD0ifQ==\n",
440 };
441
442 auto const manifest =
443 "JAAAAAFxIe1FtwmimvGtH2iCcMJqC9gVFKilGfw1/"
444 "vCxHXXLplc2GnMhAkE1agqXxBwD"
445 "wDbID6OMSYuM0FDAlpAgNk8SKFn7MO2fdkcwRQIhAOngu9sAKqXYouJ+l2V0W+"
446 "sAOkVB"
447 "+ZRS6PShlJAfUsXfAiBsVJGesaadOJc/"
448 "aAZokS1vymGmVrlHPKWX3Yywu6in8HASQKPu"
449 "gBD67kMaRFGvmpATHlGKJdvDFlWPYy5AqDedFv5TJa2w0i21eq3MYywLVJZnFO"
450 "r7C0kw"
451 "2AiTzSCjIzditQ8=";
452
453 auto const token = loadValidatorToken(tokenBlob);
454 BEAST_EXPECT(token);
455 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
456 BEAST_EXPECT(test::equal(token->validationSecret, *valSecret));
457 // NOLINTNEXTLINE(bugprone-unchecked-optional-access)
458 BEAST_EXPECT(token->manifest == manifest);
459 }
460 {
461 std::vector<std::string> const badToken = {"bad token"};
462 BEAST_EXPECT(!loadValidatorToken(badToken));
463 }
464 }
465
466 void
468 {
469 testcase("Versioning");
470
472 auto const pk = derivePublicKey(KeyType::ed25519, sk);
473
475 auto const spk = derivePublicKey(KeyType::secp256k1, ssk);
476
477 auto buildManifestObject = [&](std::uint16_t version) {
479 st[sfSequence] = 3;
480 st[sfPublicKey] = pk;
481 st[sfSigningPubKey] = spk;
482
483 if (version != 0)
484 st[sfVersion] = version;
485
486 sign(st, HashPrefix::manifest, KeyType::ed25519, sk, sfMasterSignature);
488
489 Serializer s;
490 st.add(s);
491
492 return std::string(static_cast<char const*>(s.data()), s.size());
493 };
494
495 // We understand version 0 manifests:
496 BEAST_EXPECT(deserializeManifest(buildManifestObject(0)));
497
498 // We don't understand any other versions:
499 BEAST_EXPECT(!deserializeManifest(buildManifestObject(1)));
500 BEAST_EXPECT(!deserializeManifest(buildManifestObject(2001)));
501 }
502
503 void
505 {
507
508 std::uint32_t sequence = 0;
509
510 // public key with invalid type
511 std::array<std::uint8_t, 33> const badKey{
512 0x99, 0x30, 0xE7, 0xFC, 0x9D, 0x56, 0xBB, 0x25, 0xD6, 0x89, 0x3B,
513 0xA3, 0xF3, 0x17, 0xAE, 0x5B, 0xCF, 0x33, 0xB3, 0x29, 0x1B, 0xD6,
514 0x3D, 0xB3, 0x26, 0x54, 0xA3, 0x13, 0x22, 0x2F, 0x7F, 0xD0, 0x20};
515
516 // Short public key:
517 std::array<std::uint8_t, 16> const shortKey{
518 0x03,
519 0x30,
520 0xE7,
521 0xFC,
522 0x9D,
523 0x56,
524 0xBB,
525 0x25,
526 0xD6,
527 0x89,
528 0x3B,
529 0xA3,
530 0xF3,
531 0x17,
532 0xAE,
533 0x5B};
534
535 auto toString = [](STObject const& st) {
536 Serializer s;
537 st.add(s);
538
539 return std::string(static_cast<char const*>(s.data()), s.size());
540 };
541
542 for (auto const keyType : keyTypes)
543 {
544 auto const sk = generateSecretKey(keyType, randomSeed());
545 auto const pk = derivePublicKey(keyType, sk);
546
547 for (auto const sKeyType : keyTypes)
548 {
549 auto const ssk = generateSecretKey(sKeyType, randomSeed());
550 auto const spk = derivePublicKey(sKeyType, ssk);
551
552 auto buildManifestObject = [&](std::uint32_t seq,
554 bool noSigningPublic = false,
555 bool noSignature = false) {
557 st[sfSequence] = seq;
558 st[sfPublicKey] = pk;
559
560 if (domain)
561 st[sfDomain] = makeSlice(*domain);
562
563 if (!noSigningPublic)
564 st[sfSigningPubKey] = spk;
565
566 sign(st, HashPrefix::manifest, keyType, sk, sfMasterSignature);
567
568 if (!noSignature)
569 sign(st, HashPrefix::manifest, sKeyType, ssk);
570
571 return st;
572 };
573
574 {
575 testcase << "deserializeManifest: normal manifest (" << to_string(keyType)
576 << " + " << to_string(sKeyType) << ")";
577
578 { // valid manifest without domain
579 auto const st = buildManifestObject(++sequence, std::nullopt);
580
581 auto const m = toString(st);
582 auto const manifest = deserializeManifest(m);
583
584 BEAST_EXPECT(manifest);
585 // NOLINTBEGIN(bugprone-unchecked-optional-access)
586 BEAST_EXPECT(manifest->masterKey == pk);
587 BEAST_EXPECT(manifest->signingKey == spk);
588 BEAST_EXPECT(manifest->sequence == sequence);
589 BEAST_EXPECT(manifest->serialized == m);
590 BEAST_EXPECT(manifest->domain.empty());
591 BEAST_EXPECT(manifest->verify());
592 // NOLINTEND(bugprone-unchecked-optional-access)
593 }
594
595 { // invalid manifest (empty domain)
596 auto const st = buildManifestObject(++sequence, std::string{});
597
598 BEAST_EXPECT(!deserializeManifest(toString(st)));
599 }
600
601 { // invalid manifest (domain too short)
602 auto const st = buildManifestObject(++sequence, std::string{"a.b"});
603 BEAST_EXPECT(!deserializeManifest(toString(st)));
604 }
605 { // invalid manifest (domain too long)
606 std::string const s(254, 'a');
607 auto const st = buildManifestObject(++sequence, s + ".example.com");
608 BEAST_EXPECT(!deserializeManifest(toString(st)));
609 }
610 { // invalid manifest (domain component too long)
611 std::string const s(72, 'a');
612 auto const st = buildManifestObject(++sequence, s + ".example.com");
613 BEAST_EXPECT(!deserializeManifest(toString(st)));
614 }
615
616 auto const st = buildManifestObject(++sequence, std::string{"example.com"});
617
618 {
619 // valid manifest with domain
620 auto const m = toString(st);
621 auto const manifest = deserializeManifest(m);
622
623 BEAST_EXPECT(manifest);
624 // NOLINTBEGIN(bugprone-unchecked-optional-access)
625 BEAST_EXPECT(manifest->masterKey == pk);
626 BEAST_EXPECT(manifest->signingKey == spk);
627 BEAST_EXPECT(manifest->sequence == sequence);
628 BEAST_EXPECT(manifest->serialized == m);
629 BEAST_EXPECT(manifest->domain == "example.com");
630 BEAST_EXPECT(manifest->verify());
631 // NOLINTEND(bugprone-unchecked-optional-access)
632 }
633 {
634 // valid manifest with invalid signature
635 auto badSigSt = st;
636 badSigSt[sfSequence] = sequence + 1;
637
638 auto const m = toString(badSigSt);
639 auto const manifest = deserializeManifest(m);
640
641 BEAST_EXPECT(manifest);
642 // NOLINTBEGIN(bugprone-unchecked-optional-access)
643 BEAST_EXPECT(manifest->masterKey == pk);
644 BEAST_EXPECT(manifest->signingKey == spk);
645 BEAST_EXPECT(manifest->sequence == sequence + 1);
646 BEAST_EXPECT(manifest->serialized == m);
647 BEAST_EXPECT(manifest->domain == "example.com");
648 BEAST_EXPECT(!manifest->verify());
649 // NOLINTEND(bugprone-unchecked-optional-access)
650 }
651 {
652 // reject missing sequence
653 auto badSt = st;
654 BEAST_EXPECT(badSt.delField(sfSequence));
655 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
656 }
657 {
658 // reject missing public key
659 auto badSt = st;
660 BEAST_EXPECT(badSt.delField(sfPublicKey));
661 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
662 }
663 {
664 // reject invalid public key type
665 auto badSt = st;
666 badSt[sfPublicKey] = makeSlice(badKey);
667 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
668 }
669 {
670 // reject short public key
671 auto badSt = st;
672 badSt[sfPublicKey] = makeSlice(shortKey);
673 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
674 }
675 {
676 // reject missing signing public key
677 auto badSt = st;
678 BEAST_EXPECT(badSt.delField(sfSigningPubKey));
679 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
680 }
681 {
682 // reject invalid signing public key type
683 auto badSt = st;
684 badSt[sfSigningPubKey] = makeSlice(badKey);
685 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
686 }
687 {
688 // reject short signing public key
689 auto badSt = st;
690 badSt[sfSigningPubKey] = makeSlice(shortKey);
691 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
692 }
693 {
694 // reject missing signature
695 auto badSt = st;
696 BEAST_EXPECT(badSt.delField(sfMasterSignature));
697 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
698 }
699 {
700 // reject missing signing key signature
701 auto badSt = st;
702 BEAST_EXPECT(badSt.delField(sfSignature));
703 BEAST_EXPECT(!deserializeManifest(toString(badSt)));
704 }
705 {
706 // reject matching master & ephemeral keys
708 st[sfSequence] = 314159;
709 st[sfPublicKey] = pk;
710 st[sfSigningPubKey] = pk;
711
712 sign(st, HashPrefix::manifest, keyType, sk, sfMasterSignature);
713
714 sign(st, HashPrefix::manifest, sKeyType, sk);
715
716 BEAST_EXPECT(!deserializeManifest(toString(st)));
717 }
718 }
719
720 {
721 testcase << "deserializeManifest: revocation manifest (" << to_string(keyType)
722 << " + " << to_string(sKeyType) << ")";
723
724 // valid revocation
725 {
726 auto const st = buildManifestObject(
728
729 auto const m = toString(st);
730 auto const manifest = deserializeManifest(m);
731
732 BEAST_EXPECT(manifest);
733 // NOLINTBEGIN(bugprone-unchecked-optional-access)
734 BEAST_EXPECT(manifest->masterKey == pk);
735
736 // Since this manifest is revoked, it should not have a signingKey
737 BEAST_EXPECT(!manifest->signingKey);
738 BEAST_EXPECT(manifest->revoked());
739 BEAST_EXPECT(manifest->domain.empty());
740 BEAST_EXPECT(manifest->serialized == m);
741 BEAST_EXPECT(manifest->verify());
742 // NOLINTEND(bugprone-unchecked-optional-access)
743 }
744
745 { // can't specify an ephemeral signing key
746 auto const st = buildManifestObject(
748
749 BEAST_EXPECT(!deserializeManifest(toString(st)));
750 }
751 { // can't specify an ephemeral signature
752 auto const st = buildManifestObject(
754
755 BEAST_EXPECT(!deserializeManifest(toString(st)));
756 }
757 { // can't specify an ephemeral key & signature
758 auto const st = buildManifestObject(
760
761 BEAST_EXPECT(!deserializeManifest(toString(st)));
762 }
763 }
764 }
765 }
766 }
767
768 void
770 {
771 testcase("Manifest Domain Names");
772
774 auto const pk1 = derivePublicKey(KeyType::secp256k1, sk1);
775
777 auto const pk2 = derivePublicKey(KeyType::secp256k1, sk2);
778
779 auto test = [&](std::string domain) {
781 st[sfSequence] = 7;
782 st[sfPublicKey] = pk1;
783 st[sfDomain] = makeSlice(domain);
784 st[sfSigningPubKey] = pk2;
785
786 sign(st, HashPrefix::manifest, KeyType::secp256k1, sk1, sfMasterSignature);
788
789 Serializer s;
790 st.add(s);
791
792 return deserializeManifest(std::string(static_cast<char const*>(s.data()), s.size()));
793 };
794
795 BEAST_EXPECT(test("example.com"));
796 BEAST_EXPECT(test("test.example.com"));
797 BEAST_EXPECT(test("example-domain.com"));
798 BEAST_EXPECT(test("xn--mxavchb.gr"));
799 BEAST_EXPECT(test("test.xn--mxavchb.gr"));
800 BEAST_EXPECT(test("123.gr"));
801 BEAST_EXPECT(test("x.yz"));
802 BEAST_EXPECT(test(std::string(63, 'a') + ".example.com"));
803 BEAST_EXPECT(test(std::string(63, 'a') + "." + std::string(63, 'b')));
804
805 // No period
806 BEAST_EXPECT(!test("example"));
807
808 // Leading period:
809 BEAST_EXPECT(!test(".com"));
810 BEAST_EXPECT(!test(".example.com"));
811
812 // A trailing period is technically valid but we don't allow it
813 BEAST_EXPECT(!test("example.com."));
814
815 // A component can't start or end with a dash
816 BEAST_EXPECT(!test("-example.com"));
817 BEAST_EXPECT(!test("example-.com"));
818
819 // Empty component:
820 BEAST_EXPECT(!test("double..periods.example.com"));
821
822 // TLD too short or too long:
823 BEAST_EXPECT(!test("example.x"));
824 BEAST_EXPECT(!test("example." + std::string(64, 'a')));
825
826 // Invalid characters:
827 BEAST_EXPECT(!test("example.com-org"));
828 BEAST_EXPECT(!test("bang!.com"));
829 BEAST_EXPECT(!test("bang!.example.com"));
830
831 // Too short
832 BEAST_EXPECT(!test("a.b"));
833
834 // Single component too long:
835 BEAST_EXPECT(!test(std::string(64, 'a') + ".com"));
836 BEAST_EXPECT(!test(std::string(64, 'a') + ".example.com"));
837
838 // Multiple components too long:
839 BEAST_EXPECT(!test(std::string(64, 'a') + "." + std::string(64, 'b')));
840 BEAST_EXPECT(!test(std::string(64, 'a') + "." + std::string(64, 'b')));
841
842 // Overall too long:
843 BEAST_EXPECT(!test(std::string(63, 'a') + "." + std::string(63, 'b') + ".example.com"));
844 }
845
846 void
847 run() override
848 {
849 ManifestCache cache;
850 {
851 testcase("apply");
852
853 auto const sk_a = randomSecretKey();
854 auto const pk_a = derivePublicKey(KeyType::ed25519, sk_a);
855 auto const kp_a0 = randomKeyPair(KeyType::secp256k1);
856 auto const kp_a1 = randomKeyPair(KeyType::secp256k1);
857 auto const s_a0 =
858 makeManifest(sk_a, KeyType::ed25519, kp_a0.second, KeyType::secp256k1, 0);
859 auto const s_a1 =
860 makeManifest(sk_a, KeyType::ed25519, kp_a1.second, KeyType::secp256k1, 1);
861 auto const s_a2 =
862 makeManifest(sk_a, KeyType::ed25519, kp_a1.second, KeyType::secp256k1, 2);
863 auto const s_aMax = makeRevocation(sk_a, KeyType::ed25519);
864
865 auto const sk_b = randomSecretKey();
866 auto const kp_b0 = randomKeyPair(KeyType::secp256k1);
867 auto const kp_b1 = randomKeyPair(KeyType::secp256k1);
868 auto const kp_b2 = randomKeyPair(KeyType::secp256k1);
869 auto const s_b0 =
870 makeManifest(sk_b, KeyType::ed25519, kp_b0.second, KeyType::secp256k1, 0);
871 auto const s_b1 = makeManifest(
872 sk_b,
874 kp_b1.second,
876 1,
877 true); // invalidSig
878 auto const s_b2 =
879 makeManifest(sk_b, KeyType::ed25519, kp_b2.second, KeyType::ed25519, 2);
880
881 auto const fake = s_b2.serialized + '\0';
882
883 // applyManifest should accept new manifests with
884 // higher sequence numbers
885 auto const seq0 = cache.sequence();
886 BEAST_EXPECT(cache.applyManifest(clone(s_a0)) == ManifestDisposition::accepted);
887 BEAST_EXPECT(cache.sequence() > seq0);
888
889 auto const seq1 = cache.sequence();
890 BEAST_EXPECT(cache.applyManifest(clone(s_a0)) == ManifestDisposition::stale);
891 BEAST_EXPECT(cache.sequence() == seq1);
892
893 BEAST_EXPECT(cache.applyManifest(clone(s_a1)) == ManifestDisposition::accepted);
894 BEAST_EXPECT(cache.applyManifest(clone(s_a1)) == ManifestDisposition::stale);
895 BEAST_EXPECT(cache.applyManifest(clone(s_a0)) == ManifestDisposition::stale);
896
897 BEAST_EXPECT(cache.applyManifest(clone(s_a2)) == ManifestDisposition::badEphemeralKey);
898
899 // applyManifest should accept manifests with max sequence numbers
900 // that revoke the master public key
901 BEAST_EXPECT(!cache.revoked(pk_a));
902 BEAST_EXPECT(s_aMax.revoked());
903 BEAST_EXPECT(cache.applyManifest(clone(s_aMax)) == ManifestDisposition::accepted);
904 BEAST_EXPECT(cache.applyManifest(clone(s_aMax)) == ManifestDisposition::stale);
905 BEAST_EXPECT(cache.applyManifest(clone(s_a1)) == ManifestDisposition::stale);
906 BEAST_EXPECT(cache.applyManifest(clone(s_a0)) == ManifestDisposition::stale);
907 BEAST_EXPECT(cache.revoked(pk_a));
908
909 // applyManifest should reject manifests with invalid signatures
910 BEAST_EXPECT(cache.applyManifest(clone(s_b0)) == ManifestDisposition::accepted);
911 BEAST_EXPECT(cache.applyManifest(clone(s_b0)) == ManifestDisposition::stale);
912 BEAST_EXPECT(!deserializeManifest(fake));
913 BEAST_EXPECT(cache.applyManifest(clone(s_b1)) == ManifestDisposition::invalid);
914 BEAST_EXPECT(cache.applyManifest(clone(s_b2)) == ManifestDisposition::accepted);
915
916 auto const s_c0 = makeManifest(
918 BEAST_EXPECT(cache.applyManifest(clone(s_c0)) == ManifestDisposition::badMasterKey);
919 }
920
921 testLoadStore(cache);
923 testGetKeys();
928 }
929};
930
931BEAST_DEFINE_TESTSUITE(Manifest, app, xrpl);
932
933} // namespace test
934} // namespace xrpl
T begin(T... args)
A testsuite class.
Definition suite.h:51
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:150
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:519
Remembers manifests with the highest sequence number.
Definition Manifest.h:235
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::optional< PublicKey > getSigningKey(PublicKey const &pk) const
Returns master key's current signing key.
ManifestDisposition applyManifest(Manifest m)
Add manifest to cache.
PublicKey getMasterKey(PublicKey const &pk) const
Returns ephemeral signing key's master public key.
std::uint32_t sequence() const
A monotonically increasing number used to detect new manifests.
Definition Manifest.h:255
void save(DatabaseCon &dbCon, std::string const &dbTable, std::function< bool(PublicKey const &)> const &isTrusted)
Save cached manifests to database.
bool revoked(PublicKey const &pk) const
Returns true if master key has been revoked in a manifest.
A public key.
Definition PublicKey.h:42
void addWithoutSigningFields(Serializer &s) const
Definition STObject.h:971
void add(Serializer &s) const override
Definition STObject.cpp:119
A secret key.
Definition SecretKey.h:18
Slice slice() const noexcept
Definition Serializer.h:44
std::size_t size() const noexcept
Definition Serializer.h:50
void const * data() const noexcept
Definition Serializer.h:56
static Manifest clone(Manifest const &m)
void testLoadStore(ManifestCache &m)
static PublicKey randomMasterKey()
void run() override
Runs the suite.
static std::string makeManifestString(PublicKey const &pk, SecretKey const &sk, PublicKey const &spk, SecretKey const &ssk, int seq)
std::string makeRevocationString(SecretKey const &sk, KeyType type, bool invalidSig=false)
static void setupDatabaseDir(boost::filesystem::path const &dbPath)
static void cleanupDatabaseDir(boost::filesystem::path const &dbPath)
Manifest makeManifest(SecretKey const &sk, KeyType type, SecretKey const &ssk, KeyType stype, int seq, bool invalidSig=false)
static PublicKey randomNode()
static boost::filesystem::path getDatabasePath()
Manifest makeRevocation(SecretKey const &sk, KeyType type, bool invalidSig=false)
A transaction testing environment.
Definition Env.h:122
Application & app()
Definition Env.h:259
ManualTimeKeeper & timeKeeper()
Definition Env.h:271
beast::Journal const journal
Definition Env.h:163
Set the domain on a JTx.
Definition domain.h:11
Set the regular signature on a JTx.
Definition sig.h:15
T end(T... args)
T equal(T... args)
T is_same_v
T max(T... args)
void sign(Json::Value &jv, Account const &account, Json::Value &sigObject)
Sign automatically into a specific Json field of the jv object.
Definition utility.cpp:27
bool equal(std::unique_ptr< Step > const &s1, DirectStepInfo const &dsi)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
KeyType
Definition KeyType.h:8
std::pair< PublicKey, SecretKey > randomKeyPair(KeyType type)
Create a key pair using secure random numbers.
PublicKey derivePublicKey(KeyType type, SecretKey const &sk)
Derive the public key from a secret key.
Seed randomSeed()
Create a seed using secure random numbers.
Definition Seed.cpp:47
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
bool verify(PublicKey const &publicKey, Slice const &m, Slice const &sig) noexcept
Verify a signature on a message.
SField const sfGeneric
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:92
SecretKey generateSecretKey(KeyType type, Seed const &seed)
Generate a new secret key deterministically.
std::string base64_encode(std::uint8_t const *data, std::size_t len)
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
std::optional< Manifest > deserializeManifest(Slice s, beast::Journal journal)
Constructs Manifest from serialized string.
SecretKey randomSecretKey()
Create a secret key using secure random numbers.
@ manifest
Manifest.
std::unique_ptr< DatabaseCon > makeTestWalletDB(DatabaseCon::Setup const &setup, std::string const &dbname, beast::Journal j)
makeTestWalletDB Opens a test wallet database with an arbitrary name.
Definition Wallet.cpp:17
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:215
std::optional< ValidatorToken > loadValidatorToken(std::vector< std::string > const &blob, beast::Journal journal=beast::Journal(beast::Journal::getNullSink()))
@ 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.
T push_back(T... args)
T reserve(T... args)
T size(T... args)
T sort(T... args)
boost::filesystem::path dataDir
Definition DatabaseCon.h:75
PublicKey masterKey
The master key associated with this manifest.
Definition Manifest.h:66
std::string serialized
The manifest in serialized form.
Definition Manifest.h:63
std::string domain
The domain, if one was specified in the manifest; empty otherwise.
Definition Manifest.h:78
std::optional< PublicKey > signingKey
The ephemeral key associated with this manifest.
Definition Manifest.h:72
std::uint32_t sequence
The sequence number of this manifest.
Definition Manifest.h:75
Set the sequence number on a JTx.
Definition seq.h:14