Files
rippled/include/xrpl/protocol/PublicKey.h
2026-07-13 10:40:40 +00:00

303 lines
7.0 KiB
C++

#pragma once
#include <xrpl/basics/Slice.h>
#include <xrpl/basics/StringUtilities.h>
#include <xrpl/basics/base_uint.h>
#include <xrpl/basics/contract.h>
#include <xrpl/beast/net/IPEndpoint.h>
#include <xrpl/json/json_value.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/KeyType.h>
#include <xrpl/protocol/SField.h>
#include <xrpl/protocol/STBlob.h>
#include <xrpl/protocol/STExchange.h>
#include <xrpl/protocol/UintTypes.h>
#include <xrpl/protocol/json_get_or_throw.h>
#include <xrpl/protocol/tokens.h>
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <memory>
#include <optional>
#include <ostream>
#include <sstream>
#include <string>
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 <class Hasher>
void
hash_append(Hasher& h, PublicKey const& pk)
{
h(pk.data(), pk.size());
}
template <>
struct STExchange<STBlob, PublicKey>
{
explicit STExchange() = default;
using value_type = PublicKey;
static void
get(std::optional<value_type>& t, STBlob const& u)
{
t.emplace(Slice(u.data(), u.size()));
}
static std::unique_ptr<STBlob>
set(SField const& f, PublicKey const& t)
{
return std::make_unique<STBlob>(f, t.data(), t.size());
}
};
//------------------------------------------------------------------------------
inline std::string
toBase58(TokenType type, PublicKey const& pk)
{
return encodeBase58Token(type, pk.data(), pk.size());
}
template <>
std::optional<PublicKey>
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>
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<KeyType>
publicKeyType(Slice const& slice);
[[nodiscard]] inline std::optional<KeyType>
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<PublicKey> const& publicKey = std::nullopt,
std::optional<std::string> 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<std::string>(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<PublicKey>(tokenType, b58))
return *pk;
}
Throw<JsonTypeMismatchError>(field.getJsonName(), "PublicKey");
}
} // namespace json