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