rippled
Loading...
Searching...
No Matches
WalletPropose.cpp
1#include <xrpld/rpc/Context.h>
2#include <xrpld/rpc/detail/RPCHelpers.h>
3#include <xrpld/rpc/handlers/admin/keygen/WalletPropose.h>
4
5#include <xrpl/basics/strHex.h>
6#include <xrpl/protocol/ErrorCodes.h>
7#include <xrpl/protocol/KeyType.h>
8#include <xrpl/protocol/PublicKey.h>
9#include <xrpl/protocol/RPCErr.h>
10#include <xrpl/protocol/SecretKey.h>
11#include <xrpl/protocol/Seed.h>
12#include <xrpl/protocol/jss.h>
13
14#include <cmath>
15#include <map>
16
17namespace xrpl {
18
19double
21{
22 // First, we calculate the Shannon entropy. This gives
23 // the average number of bits per symbol that we would
24 // need to encode the input.
26
27 for (auto const& c : input)
28 freq[c]++;
29
30 double se = 0.0;
31
32 for (auto const& [_, f] : freq)
33 {
34 (void)_;
35 auto x = f / input.length();
36 se += (x)*log2(x);
37 }
38
39 // We multiply it by the length, to get an estimate of
40 // the number of bits in the input. We floor because it
41 // is better to be conservative.
42 return std::floor(-se * input.length());
43}
44
45// {
46// passphrase: <string>
47// }
50{
51 return walletPropose(context.params);
52}
53
56{
59 bool rippleLibSeed = false;
60
61 if (params.isMember(jss::key_type))
62 {
63 if (!params[jss::key_type].isString())
64 {
65 return RPC::expected_field_error(jss::key_type, "string");
66 }
67
68 keyType = keyTypeFromString(params[jss::key_type].asString());
69
70 if (!keyType)
72 }
73
74 // ripple-lib encodes seed used to generate an Ed25519 wallet in a
75 // non-standard way. While we never encode seeds that way, we try
76 // to detect such keys to avoid user confusion.
77 {
78 if (params.isMember(jss::passphrase))
79 {
80 seed = RPC::parseRippleLibSeed(params[jss::passphrase]);
81 }
82 else if (params.isMember(jss::seed))
83 {
84 seed = RPC::parseRippleLibSeed(params[jss::seed]);
85 }
86
87 if (seed)
88 {
89 rippleLibSeed = true;
90
91 // If the user *explicitly* requests a key type other than
92 // Ed25519 we return an error.
94 return rpcError(rpcBAD_SEED);
95
96 keyType = KeyType::ed25519;
97 }
98 }
99
100 if (!seed)
101 {
102 if (params.isMember(jss::passphrase) || params.isMember(jss::seed) ||
103 params.isMember(jss::seed_hex))
104 {
105 Json::Value err;
106
107 seed = RPC::getSeedFromRPC(params, err);
108
109 if (!seed)
110 return err;
111 }
112 else
113 {
114 seed = randomSeed();
115 }
116 }
117
118 if (!keyType)
119 keyType = KeyType::secp256k1;
120
121 auto const publicKey = generateKeyPair(*keyType, *seed).first;
122
124
125 auto const seed1751 = seedAs1751(*seed);
126 auto const seedHex = strHex(*seed);
127 auto const seedBase58 = toBase58(*seed);
128
129 obj[jss::master_seed] = seedBase58;
130 obj[jss::master_seed_hex] = seedHex;
131 obj[jss::master_key] = seed1751;
132 obj[jss::account_id] = toBase58(calcAccountID(publicKey));
133 obj[jss::public_key] = toBase58(TokenType::AccountPublic, publicKey);
134 obj[jss::key_type] = to_string(*keyType);
135 obj[jss::public_key_hex] = strHex(publicKey);
136
137 // If a passphrase was specified, and it was hashed and used as a seed
138 // run a quick entropy check and add an appropriate warning, because
139 // "brain wallets" can be easily attacked.
140 if (!rippleLibSeed && params.isMember(jss::passphrase))
141 {
142 auto const passphrase = params[jss::passphrase].asString();
143
144 if (passphrase != seed1751 && passphrase != seedBase58 && passphrase != seedHex)
145 {
146 // 80 bits of entropy isn't bad, but it's better to
147 // err on the side of caution and be conservative.
148 if (estimate_entropy(passphrase) < 80.0)
149 {
150 obj[jss::warning] =
151 "This wallet was generated using a user-supplied "
152 "passphrase that has low entropy and is vulnerable "
153 "to brute-force attacks.";
154 }
155 else
156 {
157 obj[jss::warning] =
158 "This wallet was generated using a user-supplied "
159 "passphrase. It may be vulnerable to brute-force "
160 "attacks.";
161 }
162 }
163 }
164
165 return obj;
166}
167
168} // namespace xrpl
Represents a JSON value.
Definition json_value.h:130
std::string asString() const
Returns the unquoted string value.
bool isMember(char const *key) const
Return true if the object has a member named key.
T floor(T... args)
@ objectValue
object value (collection of name/value pairs).
Definition json_value.h:26
Json::Value expected_field_error(std::string const &name, std::string const &type)
Definition ErrorCodes.h:297
std::optional< Seed > parseRippleLibSeed(Json::Value const &value)
Parses a RippleLib seed from RPC parameters.
std::optional< Seed > getSeedFromRPC(Json::Value const &params, Json::Value &error)
Extracts a Seed from RPC parameters.
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:5
std::optional< KeyType > keyTypeFromString(std::string const &s)
Definition KeyType.h:14
Seed randomSeed()
Create a seed using secure random numbers.
Definition Seed.cpp:47
std::string to_string(base_uint< Bits, Tag > const &a)
Definition base_uint.h:602
std::string strHex(FwdIt begin, FwdIt end)
Definition strHex.h:10
std::string toBase58(AccountID const &v)
Convert AccountID to base58 checked string.
Definition AccountID.cpp:92
Json::Value doWalletPropose(RPC::JsonContext &context)
double estimate_entropy(std::string const &input)
std::string seedAs1751(Seed const &seed)
Encode a Seed in RFC1751 format.
Definition Seed.cpp:115
std::pair< PublicKey, SecretKey > generateKeyPair(KeyType type, Seed const &seed)
Generate a key pair deterministically.
Json::Value walletPropose(Json::Value const &params)
AccountID calcAccountID(PublicKey const &pk)
Json::Value rpcError(error_code_i iError)
Definition RPCErr.cpp:12
@ rpcBAD_SEED
Definition ErrorCodes.h:79
@ rpcINVALID_PARAMS
Definition ErrorCodes.h:64
T length(T... args)
Json::Value params
Definition Context.h:43
T value_or(T... args)