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 (int i = 0; i < slice.size(); ++i)
91 {
92 static constexpr char kHex[] = "0123456789ABCDEF";
93 s += kHex[((slice[i] & 0xf0) >> 4)];
94 s += kHex[((slice[i] & 0x0f) >> 0)];
95 }
96 return s;
97}
98
113{
114 using uint264 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<
115 264,
116 264,
117 boost::multiprecision::signed_magnitude,
118 boost::multiprecision::unchecked,
119 void>>;
120
121 static uint264 const kG(
122 "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"); // NOLINT(readability-identifier-naming)
123
124 // The format of a signature should be:
125 // <30> <len> [ <02> <lenR> <R> ] [ <02> <lenS> <S> ]
126 if ((sig.size() < 8) || (sig.size() > 72))
127 return std::nullopt;
128 if ((sig[0] != 0x30) || (sig[1] != (sig.size() - 2)))
129 return std::nullopt;
130 Slice p = sig + 2;
131 auto r = sigPart(p);
132 auto s = sigPart(p);
133 if (!r || !s || !p.empty())
134 return std::nullopt;
135
136 uint264 const rNum(sliceToHex(*r));
137 if (rNum >= kG)
138 return std::nullopt;
139
140 uint264 const sNum(sliceToHex(*s));
141 if (sNum >= kG)
142 return std::nullopt;
143
144 // (R,S) and (R,G-S) are canonical,
145 // but is fully canonical when S <= G-S
146 auto const Sp = kG - sNum; // NOLINT(readability-identifier-naming)
147 if (sNum > Sp)
150}
151
152static bool
154{
155 if (sig.size() != 64)
156 return false;
157 // Big-endian Order, the Ed25519 subgroup order
158 // NOLINTNEXTLINE(readability-identifier-naming)
159 std::uint8_t const Order[] = {
160 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
161 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xDE, 0xF9, 0xDE, 0xA2, 0xF7,
162 0x9C, 0xD6, 0x58, 0x12, 0x63, 0x1A, 0x5C, 0xF5, 0xD3, 0xED,
163 };
164 // Take the second half of signature
165 // and byte-reverse it to big-endian.
166 auto const le = sig.data() + 32;
167 std::uint8_t S[32]; // NOLINT(readability-identifier-naming)
168 std::reverse_copy(le, le + 32, S);
169 // Must be less than Order
170 return std::lexicographical_compare(S, S + 32, Order, Order + 32);
171}
172
173//------------------------------------------------------------------------------
174
176{
177 if (slice.size() < kSize)
178 {
180 "PublicKey::PublicKey - Input slice cannot be an undersized "
181 "buffer");
182 }
183
184 if (!publicKeyType(slice))
185 logicError("PublicKey::PublicKey invalid type");
186 std::memcpy(buf_, slice.data(), kSize);
187}
188
190{
191 std::memcpy(buf_, other.buf_, kSize);
192}
193
196{
197 if (this != &other)
198 {
199 std::memcpy(buf_, other.buf_, kSize);
200 }
201
202 return *this;
203}
204
205//------------------------------------------------------------------------------
206
208publicKeyType(Slice const& slice)
209{
210 if (slice.size() == 33)
211 {
212 if (slice[0] == 0xED)
213 return KeyType::Ed25519;
214
215 if (slice[0] == kEcCompressedPrefixEvenY || slice[0] == kEcCompressedPrefixOddY)
216 return KeyType::Secp256k1;
217 }
218
219 return std::nullopt;
220}
221
222bool
224 PublicKey const& publicKey,
225 uint256 const& digest,
226 Slice const& sig,
227 bool mustBeFullyCanonical) noexcept
228{
229 if (publicKeyType(publicKey) != KeyType::Secp256k1)
230 logicError("sign: secp256k1 required for digest signing");
231 auto const canonicality = ecdsaCanonicality(sig);
232 if (!canonicality)
233 return false;
234 if (mustBeFullyCanonical && (*canonicality != ECDSACanonicality::FullyCanonical))
235 return false;
236
237 secp256k1_pubkey pubkeyImp;
238 if (secp256k1_ec_pubkey_parse(
240 &pubkeyImp,
241 reinterpret_cast<unsigned char const*>(publicKey.data()),
242 publicKey.size()) != 1)
243 return false;
244
245 secp256k1_ecdsa_signature sigImp;
246 if (secp256k1_ecdsa_signature_parse_der(
248 &sigImp,
249 reinterpret_cast<unsigned char const*>(sig.data()),
250 sig.size()) != 1)
251 return false;
252 if (*canonicality != ECDSACanonicality::FullyCanonical)
253 {
254 secp256k1_ecdsa_signature sigNorm;
255 if (secp256k1_ecdsa_signature_normalize(secp256k1Context(), &sigNorm, &sigImp) != 1)
256 return false;
257 return secp256k1_ecdsa_verify(
259 &sigNorm,
260 reinterpret_cast<unsigned char const*>(digest.data()),
261 &pubkeyImp) == 1;
262 }
263 return secp256k1_ecdsa_verify(
265 &sigImp,
266 reinterpret_cast<unsigned char const*>(digest.data()),
267 &pubkeyImp) == 1;
268}
269
270bool
271verify(PublicKey const& publicKey, Slice const& m, Slice const& sig) noexcept
272{
273 if (auto const type = publicKeyType(publicKey))
274 {
275 if (*type == KeyType::Secp256k1)
276 {
277 return verifyDigest(publicKey, sha512Half(m), sig);
278 }
279 if (*type == KeyType::Ed25519)
280 {
281 if (!ed25519Canonical(sig))
282 return false;
283
284 // We internally prefix Ed25519 keys with a 0xED
285 // byte to distinguish them from secp256k1 keys
286 // so when verifying the signature, we need to
287 // first strip that prefix.
288 return ed25519_sign_open(m.data(), m.size(), publicKey.data() + 1, sig.data()) == 0;
289 }
290 }
291 return false;
292}
293
294NodeID
296{
297 static_assert(NodeID::kBytes == sizeof(RipeshaHasher::result_type));
298
300 h(pk.data(), pk.size());
301 return NodeID::fromRaw(static_cast<RipeshaHasher::result_type>(h));
302}
303
304} // namespace xrpl
static BaseUInt fromRaw(Container const &c)
Definition base_uint.h:294
static constexpr std::size_t kBytes
Definition base_uint.h:89
A public key.
Definition PublicKey.h:42
std::uint8_t const * data() const noexcept
Definition PublicKey.h:67
static std::size_t size() noexcept
Definition PublicKey.h:73
std::uint8_t buf_[kSize]
Definition PublicKey.h:47
static constexpr std::size_t kSize
Definition PublicKey.h:46
Slice slice() const noexcept
Definition PublicKey.h:103
PublicKey()=delete
PublicKey & operator=(PublicKey const &other)
An immutable linear range of bytes.
Definition Slice.h:26
bool empty() const noexcept
Return true if the byte range is empty.
Definition Slice.h:50
std::uint8_t const * data() const noexcept
Return a pointer to beginning of the storage.
Definition Slice.h:78
std::size_t size() const noexcept
Returns the number of bytes in the storage.
Definition Slice.h:61
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:367
static Hasher::result_type digest(void const *data, std::size_t size) noexcept
Definition tokens.cpp:139
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:204
std::optional< AccountID > parseBase58(std::string const &s)
Parse AccountID from checked, base58 string.
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.
constexpr std::uint8_t kEcCompressedPrefixOddY
Compressed EC point prefix for odd y-coordinate.
Definition Protocol.h:370
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:648
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.
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:18
static std::string sliceToHex(Slice const &slice)
Definition PublicKey.cpp:77
std::string decodeBase58Token(std::string const &s, TokenType type)
Definition tokens.cpp:188
BaseUInt< 256 > uint256
Definition base_uint.h:562
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
BaseUInt< 160, detail::NodeIDTag > NodeID
NodeID is a 160-bit hash representing one node.
Definition UintTypes.h:39
T reserve(T... args)
T reverse_copy(T... args)
Returns the RIPEMD-160 digest of the SHA256 hash of the message.
Definition digest.h:116
std::array< std::uint8_t, 20 > result_type
Definition digest.h:123