#pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace xrpl { /** * A public key. * * Public keys are used in the public-key cryptography * system used to verify signatures attached to messages. * * The format of the public key is XRPL specific, * information needed to determine the cryptosystem * parameters used is stored inside the key. * * As of this writing two systems are supported: * * secp256k1 * ed25519 * * secp256k1 public keys consist of a 33 byte * compressed public key, with the lead byte equal * to 0x02 or 0x03. * * The ed25519 public keys consist of a 1 byte * prefix constant 0xED, followed by 32 bytes of * public key data. */ class PublicKey { protected: // All the constructed public keys are valid, non-empty and contain 33 // bytes of data. static constexpr std::size_t kSize = 33; std::uint8_t buf_[kSize]{}; // should be large enough public: using const_iterator = std::uint8_t const*; public: PublicKey() = delete; PublicKey(PublicKey const& other); PublicKey& operator=(PublicKey const& other); /** * Create a public key. * * Preconditions: * publicKeyType(slice) != std::nullopt */ explicit PublicKey(Slice const& slice); [[nodiscard]] std::uint8_t const* data() const noexcept { return buf_; } static std::size_t size() noexcept { return kSize; } [[nodiscard]] const_iterator begin() const noexcept { return buf_; } [[nodiscard]] const_iterator cbegin() const noexcept { return buf_; } [[nodiscard]] const_iterator end() const noexcept { return buf_ + kSize; } [[nodiscard]] const_iterator cend() const noexcept { return buf_ + kSize; } [[nodiscard]] Slice slice() const noexcept { return {buf_, kSize}; } operator Slice() const noexcept { return slice(); } }; /** * Print the public key to a stream. */ std::ostream& operator<<(std::ostream& os, PublicKey const& pk); inline bool operator==(PublicKey const& lhs, PublicKey const& rhs) { return std::memcmp(lhs.data(), rhs.data(), rhs.size()) == 0; } inline bool operator<(PublicKey const& lhs, PublicKey const& rhs) { return std::lexicographical_compare( lhs.data(), lhs.data() + lhs.size(), rhs.data(), rhs.data() + rhs.size()); } template void hash_append(Hasher& h, PublicKey const& pk) { h(pk.data(), pk.size()); } template <> struct STExchange { explicit STExchange() = default; using value_type = PublicKey; static void get(std::optional& t, STBlob const& u) { t.emplace(Slice(u.data(), u.size())); } static std::unique_ptr set(SField const& f, PublicKey const& t) { return std::make_unique(f, t.data(), t.size()); } }; //------------------------------------------------------------------------------ inline std::string toBase58(TokenType type, PublicKey const& pk) { return encodeBase58Token(type, pk.data(), pk.size()); } template <> std::optional parseBase58(TokenType type, std::string const& s); enum class ECDSACanonicality { Canonical, FullyCanonical }; /** * Determines the canonicality of a signature. * * A canonical signature is in its most reduced form. * For example the R and S components do not contain * additional leading zeroes. However, even in * canonical form, (R,S) and (R,G-S) are both * valid signatures for message M. * * Therefore, to prevent malleability attacks we * define a fully canonical signature as one where: * * R < G - S * * where G is the curve order. * * This routine returns std::nullopt if the format * of the signature is invalid (for example, the * points are encoded incorrectly). * * @return std::nullopt if the signature fails * validity checks. * * @note Only the format of the signature is checked, * no verification cryptography is performed. */ std::optional ecdsaCanonicality(Slice const& sig); /** * Returns the type of public key. * * @return std::nullopt If the public key does not * represent a known type. */ /** @{ */ [[nodiscard]] std::optional publicKeyType(Slice const& slice); [[nodiscard]] inline std::optional publicKeyType(PublicKey const& publicKey) { return publicKeyType(publicKey.slice()); } /** @} */ /** * Verify a secp256k1 signature on the digest of a message. */ [[nodiscard]] bool verifyDigest( PublicKey const& publicKey, uint256 const& digest, Slice const& sig, bool mustBeFullyCanonical = true) noexcept; /** * Verify a signature on a message. * With secp256k1 signatures, the data is first hashed with * SHA512-Half, and the resulting digest is signed. */ [[nodiscard]] bool verify(PublicKey const& publicKey, Slice const& m, Slice const& sig) noexcept; /** * Calculate the 160-bit node ID from a node public key. */ NodeID calcNodeID(PublicKey const&); // VFALCO This belongs in AccountID.h but // is here because of header issues AccountID calcAccountID(PublicKey const& pk); inline std::string getFingerprint( beast::IP::Endpoint const& address, std::optional const& publicKey = std::nullopt, std::optional const& id = std::nullopt) { std::stringstream ss; ss << "IP Address: " << address; if (publicKey.has_value()) { ss << ", Public Key: " << toBase58(TokenType::NodePublic, *publicKey); } if (id.has_value()) { ss << ", Id: " << id.value(); } return ss.str(); } } // namespace xrpl //------------------------------------------------------------------------------ namespace json { template <> inline xrpl::PublicKey getOrThrow(json::Value const& v, xrpl::SField const& field) { using namespace xrpl; std::string const b58 = getOrThrow(v, field); if (auto pubKeyBlob = strUnHex(b58); pubKeyBlob.has_value() && publicKeyType(makeSlice(*pubKeyBlob))) { return PublicKey{makeSlice(*pubKeyBlob)}; } for (auto const tokenType : {TokenType::NodePublic, TokenType::AccountPublic}) { if (auto const pk = parseBase58(tokenType, b58)) return *pk; } Throw(field.getJsonName(), "PublicKey"); } } // namespace json