#include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace xrpl { class PublicKey_test : public beast::unit_test::Suite { public: using blob = std::vector; template static void hexToBinary(FwdIter first, FwdIter last, Container& out) { struct Table { int val[256]{}; Table() { std::fill(val, val + 256, 0); for (int i = 0; i < 10; ++i) val['0' + i] = i; for (int i = 0; i < 6; ++i) { val['A' + i] = 10 + i; val['a' + i] = 10 + i; } } int operator[](int i) { return val[i]; } }; static Table kLut; out.reserve(std::distance(first, last) / 2); while (first != last) { auto const hi(kLut[(*first++)]); auto const lo(kLut[(*first++)]); out.push_back((hi * 16) + lo); } } static blob sig(std::string const& hex) { blob b; hexToBinary(hex.begin(), hex.end(), b); return b; } static bool check(std::optional answer, std::string const& s) { return ecdsaCanonicality(makeSlice(sig(s))) == answer; } void testCanonical() { testcase("Canonical"); // Fully canonical BEAST_EXPECT(check( ECDSACanonicality::FullyCanonical, "3045" "022100FF478110D1D4294471EC76E0157540C2181F47DEBD25D7F9E7DDCCCD47EE" "E905" "0220078F07CDAE6C240855D084AD91D1479609533C147C93B0AEF19BC9724D003F" "28")); BEAST_EXPECT(check( ECDSACanonicality::FullyCanonical, "3045" "0221009218248292F1762D8A51BE80F8A7F2CD288D810CE781D5955700DA1684DF" "1D2D" "022041A1EE1746BFD72C9760CC93A7AAA8047D52C8833A03A20EAAE92EA19717B4" "54")); BEAST_EXPECT(check( ECDSACanonicality::FullyCanonical, "3044" "02206A9E43775F73B6D1EC420E4DDD222A80D4C6DF5D1BEECC431A91B63C928B75" "81" "022023E9CC2D61DDA6F73EAA6BCB12688BEB0F434769276B3127E4044ED895C9D9" "6B")); BEAST_EXPECT(check( ECDSACanonicality::FullyCanonical, "3044" "022056E720007221F3CD4EFBB6352741D8E5A0968D48D8D032C2FBC4F6304AD1D0" "4E" "02201F39EB392C20D7801C3E8D81D487E742FA84A1665E923225BD6323847C7187" "9F")); BEAST_EXPECT(check( ECDSACanonicality::FullyCanonical, "3045" "022100FDFD5AD05518CEA0017A2DCB5C4DF61E7C73B6D3A38E7AE93210A1564E8C" "2F12" "0220214FF061CCC123C81D0BB9D0EDEA04CD40D96BF1425D311DA62A7096BB18EA" "18")); // Canonical but not fully canonical BEAST_EXPECT(check( ECDSACanonicality::Canonical, "3046" "022100F477B3FA6F31C7CB3A0D1AD94A231FDD24B8D78862EE334CEA7CD08F6CBC" "0A1B" "022100928E6BCF1ED2684679730C5414AEC48FD62282B090041C41453C1D064AF5" "97A1")); BEAST_EXPECT(check( ECDSACanonicality::Canonical, "3045" "022063E7C7CA93CB2400E413A342C027D00665F8BAB9C22EF0A7B8AE3AAF092230" "B6" "0221008F2E8BB7D09521ABBC277717B14B93170AE6465C5A1B36561099319C4BEB" "254C")); BEAST_EXPECT(check( ECDSACanonicality::Canonical, "3046" "02210099DCA1188663DDEA506A06A7B20C2B7D8C26AFF41DECE69D6C5F7C967D32" "625F" "022100897658A6B1F9EEE5D140D7A332DA0BD73BB98974EA53F6201B01C1B594F2" "86EA")); BEAST_EXPECT(check( ECDSACanonicality::Canonical, "3045" "02200855DE366E4E323AA2CE2A25674401A7D11F72EC432770D07F7B57DF7387AE" "C0" "022100DA4C6ADDEA14888858DE2AC5B91ED9050D6972BB388DEF582628CEE32869" "AE35")); // valid BEAST_EXPECT(check( ECDSACanonicality::FullyCanonical, "3006" "020101" "020102")); BEAST_EXPECT(check( ECDSACanonicality::FullyCanonical, "3044" "02203932c892e2e550f3af8ee4ce9c215a87f9bb831dcac87b2838e2c2eaa891df" "0c" "022030b61dd36543125d56b9f9f3a1f53189e5af33cdda8d77a5209aec03978fa0" "01")); BEAST_EXPECT(check( ECDSACanonicality::Canonical, "3045" "0220076045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40f9" "0a" "0221008fffd599910eefe00bc803c688eca1d2ba7f6b180620eaa03488e6585db6" "ba01")); BEAST_EXPECT(check( ECDSACanonicality::Canonical, "3046" "022100876045be6f9eca28ff1ec606b833d0b87e70b2a630f5e3a496b110967a40" "f90a" "0221008fffd599910eefe00bc803c688c2eca1d2ba7f6b180620eaa03488e6585d" "b6ba")); BEAST_EXPECT(check( std::nullopt, "3005" "0201FF" "0200")); BEAST_EXPECT(check( std::nullopt, "3006" "020101" "020202")); BEAST_EXPECT(check( std::nullopt, "3006" "020701" "020102")); BEAST_EXPECT(check( std::nullopt, "3006" "020401" "020102")); BEAST_EXPECT(check( std::nullopt, "3006" "020501" "020102")); BEAST_EXPECT(check( std::nullopt, "3006" "020201" "020102")); BEAST_EXPECT(check( std::nullopt, "3006" "020301" "020202")); BEAST_EXPECT(check( std::nullopt, "3006" "020401" "020202")); BEAST_EXPECT(check( std::nullopt, "3047" "0221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba" "6105" "022200002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e56" "6695ed")); BEAST_EXPECT(check( std::nullopt, "3144" "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61" "05" "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695" "ed")); BEAST_EXPECT(check( std::nullopt, "3045" "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61" "05" "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695" "ed")); BEAST_EXPECT(check( std::nullopt, "301F" "01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1")); BEAST_EXPECT(check( std::nullopt, "3045" "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61" "05" "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695" "ed00")); BEAST_EXPECT(check( std::nullopt, "3044" "01205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61" "05" "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695" "ed")); BEAST_EXPECT(check( std::nullopt, "3024" "0200" "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695" "ed")); BEAST_EXPECT(check( std::nullopt, "3044" "02208990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61" "05" "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695" "ed")); BEAST_EXPECT(check( std::nullopt, "3045" "0221005990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba" "6105" "02202d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695" "ed")); BEAST_EXPECT(check( std::nullopt, "3044" "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61" "05012" "02d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695e" "d")); BEAST_EXPECT(check( std::nullopt, "3024" "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61" "05" "0200")); BEAST_EXPECT(check( std::nullopt, "3044" "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61" "05" "0220fd5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e566695" "ed")); BEAST_EXPECT(check( std::nullopt, "3045" "02205990e0584b2b238e1dfaad8d6ed69ecc1a4a13ac85fc0b31d0df395eb1ba61" "05" "0221002d5876262c288beb511d061691bf26777344b702b00f8fe28621fe4e5666" "95ed")); } void testBase58(KeyType keyType) { // Try converting short, long and malformed data BEAST_EXPECT(!parseBase58(TokenType::NodePublic, "")); BEAST_EXPECT(!parseBase58(TokenType::NodePublic, " ")); BEAST_EXPECT(!parseBase58(TokenType::NodePublic, "!ty89234gh45")); auto const good = toBase58(TokenType::NodePublic, derivePublicKey(keyType, randomSecretKey())); // Short (non-empty) strings { auto s = good; // Remove all characters from the string in random order: std::hash const r; while (!s.empty()) { s.erase(r(s) % s.size(), 1); BEAST_EXPECT(!parseBase58(TokenType::NodePublic, s)); } } // Long strings for (std::size_t i = 1; i != 16; i++) { auto s = good; s.resize(s.size() + i, s[i % s.size()]); BEAST_EXPECT(!parseBase58(TokenType::NodePublic, s)); } // Strings with invalid Base58 characters for (auto c : std::string("0IOl")) { for (std::size_t i = 0; i != good.size(); ++i) { auto s = good; s[i % s.size()] = c; BEAST_EXPECT(!parseBase58(TokenType::NodePublic, s)); } } // Strings with incorrect prefix { auto s = good; for (auto c : std::string("apsrJqtv7")) { s[0] = c; BEAST_EXPECT(!parseBase58(TokenType::NodePublic, s)); } } // Try some random secret keys std::vector keys; keys.reserve(32); for (std::size_t i = 0; i != keys.capacity(); ++i) keys.emplace_back(derivePublicKey(keyType, randomSecretKey())); BEAST_EXPECT(keys.size() == 32); for (std::size_t i = 0; i != keys.size(); ++i) { auto const si = toBase58(TokenType::NodePublic, keys[i]); BEAST_EXPECT(!si.empty()); auto const ski = parseBase58(TokenType::NodePublic, si); BEAST_EXPECT(ski && (keys[i] == *ski)); for (std::size_t j = i; j != keys.size(); ++j) { BEAST_EXPECT((keys[i] == keys[j]) == (i == j)); auto const sj = toBase58(TokenType::NodePublic, keys[j]); BEAST_EXPECT((si == sj) == (i == j)); auto const skj = parseBase58(TokenType::NodePublic, sj); BEAST_EXPECT(skj && (keys[j] == *skj)); BEAST_EXPECT( (*ski == *skj) == (i == j)); // NOLINT(bugprone-unchecked-optional-access) } } } void testBase58() { testcase("Base58: secp256k1"); { auto const pk1 = derivePublicKey( KeyType::Secp256k1, generateSecretKey(KeyType::Secp256k1, generateSeed("masterpassphrase"))); auto const pk2 = parseBase58( TokenType::NodePublic, "n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9"); BEAST_EXPECT(pk2); BEAST_EXPECT(pk1 == *pk2); // NOLINT(bugprone-unchecked-optional-access) } testBase58(KeyType::Secp256k1); testcase("Base58: ed25519"); { auto const pk1 = derivePublicKey( KeyType::Ed25519, generateSecretKey(KeyType::Ed25519, generateSeed("masterpassphrase"))); auto const pk2 = parseBase58( TokenType::NodePublic, "nHUeeJCSY2dM71oxM8Cgjouf5ekTuev2mwDpc374aLMxzDLXNmjf"); BEAST_EXPECT(pk2); BEAST_EXPECT(pk1 == *pk2); // NOLINT(bugprone-unchecked-optional-access) } testBase58(KeyType::Ed25519); } void testMiscOperations() { testcase("Miscellaneous operations"); auto const pk1 = derivePublicKey( KeyType::Secp256k1, generateSecretKey(KeyType::Secp256k1, generateSeed("masterpassphrase"))); PublicKey const pk2(pk1); BEAST_EXPECT(pk1 == pk2); BEAST_EXPECT(pk2 == pk1); PublicKey pk3 = derivePublicKey( KeyType::Secp256k1, generateSecretKey(KeyType::Secp256k1, generateSeed("arbitraryPassPhrase"))); // Testing the copy assignment operation of PublicKey class pk3 = pk2; BEAST_EXPECT(pk3 == pk2); BEAST_EXPECT(pk1 == pk3); } void run() override { testBase58(); testCanonical(); testMiscOperations(); } }; BEAST_DEFINE_TESTSUITE(PublicKey, protocol, xrpl); } // namespace xrpl