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