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