xrpld
Loading...
Searching...
No Matches
PublicKey.cpp
1#include <xrpl/protocol/PublicKey.h>
2
3#include <xrpl/basics/Slice.h>
4#include <xrpl/basics/base_uint.h>
5#include <xrpl/basics/contract.h>
6#include <xrpl/basics/strHex.h>
7#include <xrpl/protocol/KeyType.h>
8#include <xrpl/protocol/Protocol.h>
9#include <xrpl/protocol/UintTypes.h>
10#include <xrpl/protocol/detail/secp256k1.h>
11#include <xrpl/protocol/digest.h>
12#include <xrpl/protocol/tokens.h>
13
14#include <boost/multiprecision/number.hpp>
15
16#include <ed25519.h>
17#include <secp256k1.h>
18
19#include <algorithm>
20#include <cstdint>
21#include <cstring>
22#include <optional>
23#include <ostream>
24#include <string>
25
26namespace xrpl {
27
29operator<<(std::ostream& os, PublicKey const& pk)
30{
31 os << strHex(pk);
32 return os;
33}
34
35template <>
38{
39 auto const result = decodeBase58Token(s, type);
40 auto const pks = makeSlice(result);
41 if (!publicKeyType(pks))
42 return std::nullopt;
43 return PublicKey(pks);
44}
45
46//------------------------------------------------------------------------------
47
48// Parse a length-prefixed number
49// Format: 0x02 <length-byte> <number>
52{
53 if (buf.size() < 3 || buf[0] != 0x02)
54 return std::nullopt;
55 auto const len = buf[1];
56 buf += 2;
57 if (len > buf.size() || len < 1 || len > 33)
58 return std::nullopt;
59 // Can't be negative
60 if ((buf[0] & 0x80) != 0)
61 return std::nullopt;
62 if (buf[0] == 0)
63 {
64 // Can't be zero
65 if (len == 1)
66 return std::nullopt;
67 // Can't be padded
68 if ((buf[1] & 0x80) == 0)
69 return std::nullopt;
70 }
71 std::optional<Slice> number = Slice(buf.data(), len);
72 buf += len;
73 return number;
74}
75
76static std::string
77sliceToHex(Slice const& slice)
78{
80 if ((slice[0] & 0x80) != 0)
81 {
82 s.reserve(2 * (slice.size() + 2));
83 s = "0x00";
84 }
85 else
86 {
87 s.reserve(2 * (slice.size() + 1));
88 s = "0x";
89 }
90 for (std::uint8_t const byte : slice)
91 {
92 static constexpr char kHex[] = "0123456789ABCDEF";
93 s += kHex[((byte & 0xf0) >> 4)];
94 s += kHex[((byte & 0x0f) >> 0)];
95 }
96 return s;
97}
98
114{
115 using uint264 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<
116 264,
117 264,
118 boost::multiprecision::signed_magnitude,
119 boost::multiprecision::unchecked,
120 void>>;
121
122 static uint264 const kG(
123 "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"); // NOLINT(readability-identifier-naming)
124
125 // The format of a signature should be:
126 // <30> <len> [ <02> <lenR> <R> ] [ <02> <lenS> <S> ]
127 if ((sig.size() < 8) || (sig.size() > 72))
128 return std::nullopt;
129 if ((sig[0] != 0x30) || (sig[1] != (sig.size() - 2)))
130 return std::nullopt;
131 Slice p = sig + 2;
132 auto r = sigPart(p);
133 auto s = sigPart(p);
134 if (!r || !s || !p.empty())
135 return std::nullopt;
136
137 uint264 const rNum(sliceToHex(*r));
138 if (rNum >= kG)
139 return std::nullopt;
140
141 uint264 const sNum(sliceToHex(*s));
142 if (sNum >= kG)
143 return std::nullopt;
144
145 // (R,S) and (R,G-S) are canonical,
146 // but is fully canonical when S <= G-S
147 auto const Sp = kG - sNum; // NOLINT(readability-identifier-naming)
148 if (sNum > Sp)
151}
152
153static bool
155{
156 if (sig.size() != 64)
157 return false;
158 // Big-endian Order, the Ed25519 subgroup order
159 // NOLINTNEXTLINE(readability-identifier-naming)
160 std::uint8_t const Order[] = {
161 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
162 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xDE, 0xF9, 0xDE, 0xA2, 0xF7,
163 0x9C, 0xD6, 0x58, 0x12, 0x63, 0x1A, 0x5C, 0xF5, 0xD3, 0xED,
164 };
165 // Take the second half of signature
166 // and byte-reverse it to big-endian.
167 auto const le = sig.data() + 32;
168 std::uint8_t S[32]; // NOLINT(readability-identifier-naming)
169 std::reverse_copy(le, le + 32, S);
170 // Must be less than Order
171 return std::lexicographical_compare(S, S + 32, Order, Order + 32);
172}
173
174//------------------------------------------------------------------------------
175
177{
178 if (slice.size() < kSize)
179 {
181 "PublicKey::PublicKey - Input slice cannot be an undersized "
182 "buffer");
183 }
184
185 if (!publicKeyType(slice))
186 logicError("PublicKey::PublicKey invalid type");
187 std::memcpy(buf_, slice.data(), kSize);
188}
189
191{
192 std::memcpy(buf_, other.buf_, kSize);
193}
194
197{
198 if (this != &other)
199 {
200 std::memcpy(buf_, other.buf_, kSize);
201 }
202
203 return *this;
204}
205
206//------------------------------------------------------------------------------
207
209publicKeyType(Slice const& slice)
210{
211 if (slice.size() == 33)
212 {
213 if (slice[0] == 0xED)
214 return KeyType::Ed25519;
215
216 if (slice[0] == kEcCompressedPrefixEvenY || slice[0] == kEcCompressedPrefixOddY)
217 return KeyType::Secp256k1;
218 }
219
220 return std::nullopt;
221}
222
223bool
225 PublicKey const& publicKey,
226 uint256 const& digest,
227 Slice const& sig,
228 bool mustBeFullyCanonical) noexcept
229{
230 if (publicKeyType(publicKey) != KeyType::Secp256k1)
231 logicError("sign: secp256k1 required for digest signing");
232 auto const canonicality = ecdsaCanonicality(sig);
233 if (!canonicality)
234 return false;
235 if (mustBeFullyCanonical && (*canonicality != ECDSACanonicality::FullyCanonical))
236 return false;
237
238 secp256k1_pubkey pubkeyImp;
239 if (secp256k1_ec_pubkey_parse(
241 &pubkeyImp,
242 reinterpret_cast<unsigned char const*>(publicKey.data()),
243 publicKey.size()) != 1)
244 return false;
245
246 secp256k1_ecdsa_signature sigImp;
247 if (secp256k1_ecdsa_signature_parse_der(
249 &sigImp,
250 reinterpret_cast<unsigned char const*>(sig.data()),
251 sig.size()) != 1)
252 return false;
253 if (*canonicality != ECDSACanonicality::FullyCanonical)
254 {
255 secp256k1_ecdsa_signature sigNorm;
256 if (secp256k1_ecdsa_signature_normalize(secp256k1Context(), &sigNorm, &sigImp) != 1)
257 return false;
258 return secp256k1_ecdsa_verify(
260 &sigNorm,
261 reinterpret_cast<unsigned char const*>(digest.data()),
262 &pubkeyImp) == 1;
263 }
264 return secp256k1_ecdsa_verify(
266 &sigImp,
267 reinterpret_cast<unsigned char const*>(digest.data()),
268 &pubkeyImp) == 1;
269}
270
271bool
272verify(PublicKey const& publicKey, Slice const& m, Slice const& sig) noexcept
273{
274 if (auto const type = publicKeyType(publicKey))
275 {
276 if (*type == KeyType::Secp256k1)
277 {
278 return verifyDigest(publicKey, sha512Half(m), sig);
279 }
280 if (*type == KeyType::Ed25519)
281 {
282 if (!ed25519Canonical(sig))
283 return false;
284
285 // We internally prefix Ed25519 keys with a 0xED
286 // byte to distinguish them from secp256k1 keys
287 // so when verifying the signature, we need to
288 // first strip that prefix.
289 return ed25519_sign_open(m.data(), m.size(), publicKey.data() + 1, sig.data()) == 0;
290 }
291 }
292 return false;
293}
294
295NodeID
297{
298 static_assert(NodeID::kBytes == sizeof(RipeshaHasher::result_type));
299
301 h(pk.data(), pk.size());
302 return NodeID::fromRaw(static_cast<RipeshaHasher::result_type>(h));
303}
304
305} // namespace xrpl
static BaseUInt fromRaw(Container const &c)
Definition base_uint.h:302
static constexpr std::size_t kBytes
Definition base_uint.h:100
A public key.
Definition PublicKey.h:53
std::uint8_t const * data() const noexcept
Definition PublicKey.h:79
static std::size_t size() noexcept
Definition PublicKey.h:85
std::uint8_t buf_[kSize]
Definition PublicKey.h:58
static constexpr std::size_t kSize
Definition PublicKey.h:57
Slice slice() const noexcept
Definition PublicKey.h:115
PublicKey()=delete
PublicKey & operator=(PublicKey const &other)
An immutable linear range of bytes.
Definition Slice.h:28
bool empty() const noexcept
Return true if the byte range is empty.
Definition Slice.h:58
std::uint8_t const * data() const noexcept
Return a pointer to beginning of the storage.
Definition Slice.h:88
std::size_t size() const noexcept
Returns the number of bytes in the storage.
Definition Slice.h:70
T lexicographical_compare(T... args)
T memcpy(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
constexpr std::uint8_t kEcCompressedPrefixEvenY
Compressed EC point prefix for even y-coordinate.
Definition Protocol.h:539
static Hasher::result_type digest(void const *data, std::size_t size) noexcept
Definition tokens.cpp:140
static bool ed25519Canonical(Slice const &sig)
bool verifyDigest(PublicKey const &publicKey, uint256 const &digest, Slice const &sig, bool mustBeFullyCanonical=true) noexcept
Verify a secp256k1 signature on the digest of a message.
sha512_half_hasher::result_type sha512Half(Args const &... args)
Returns the SHA512-Half of a series of objects.
Definition digest.h:215
std::optional< AccountID > parseBase58(std::string const &s)
Parse AccountID from checked, base58 string.
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:13
bool verify(PublicKey const &publicKey, Slice const &m, Slice const &sig) noexcept
Verify a signature on a message.
constexpr std::uint8_t kEcCompressedPrefixOddY
Compressed EC point prefix for odd y-coordinate.
Definition Protocol.h:544
std::optional< ECDSACanonicality > ecdsaCanonicality(Slice const &sig)
Determines the canonicality of a signature.
std::ostream & operator<<(std::ostream &out, BaseUInt< Bits, Tag > const &u)
Definition base_uint.h:666
void logicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
std::optional< KeyType > publicKeyType(Slice const &slice)
Returns the type of public key.
Slice makeSlice(std::array< T, N > const &a)
Definition Slice.h:228
NodeID calcNodeID(PublicKey const &)
Calculate the 160-bit node ID from a node public key.
static std::optional< Slice > sigPart(Slice &buf)
Definition PublicKey.cpp:51
secp256k1_context const * secp256k1Context()
Definition secp256k1.h:9
TokenType
Definition tokens.h:19
static std::string sliceToHex(Slice const &slice)
Definition PublicKey.cpp:77
std::string decodeBase58Token(std::string const &s, TokenType type)
Definition tokens.cpp:191
BaseUInt< 256 > uint256
Definition base_uint.h:580
BaseUInt< 160, detail::NodeIDTag > NodeID
NodeID is a 160-bit hash representing one node.
Definition UintTypes.h:47
T reserve(T... args)
T reverse_copy(T... args)
Returns the RIPEMD-160 digest of the SHA256 hash of the message.
Definition digest.h:124
std::array< std::uint8_t, 20 > result_type
Definition digest.h:131