Display warning when generating brain wallets:

A brain wallet is a standard wallet that is generated not from a
random seed but by hashing a user-supplied passphrase. Typically,
human-selected passphrases can contain insufficient entropy.

When generating a wallet from a passphrase, we include a warning
to this effect. The warning would be incorrectly displayed even
if the wallet was being generated from a seed.
This commit is contained in:
Nik Bougalis
2017-05-17 21:59:40 -07:00
parent 68b8ffdb63
commit fa72795d84
2 changed files with 68 additions and 22 deletions

View File

@@ -108,31 +108,42 @@ Json::Value walletPropose (Json::Value const& params)
Json::Value obj (Json::objectValue);
obj[jss::master_seed] = toBase58 (*seed);
obj[jss::master_seed_hex] = strHex (seed->data(), seed->size());
obj[jss::master_key] = seedAs1751 (*seed);
auto const seed1751 = seedAs1751 (*seed);
auto const seedHex = strHex (seed->data(), seed->size());
auto const seedBase58 = toBase58 (*seed);
obj[jss::master_seed] = seedBase58;
obj[jss::master_seed_hex] = seedHex;
obj[jss::master_key] = seed1751;
obj[jss::account_id] = toBase58(calcAccountID(publicKey));
obj[jss::public_key] = toBase58(TOKEN_ACCOUNT_PUBLIC, publicKey);
obj[jss::key_type] = to_string (keyType);
obj[jss::public_key_hex] = strHex (publicKey.data(), publicKey.size());
// If a passphrase was specified, and it was hashed and used as a seed
// run a quick entropy check and add an appropriate warning, because
// "brain wallets" can be easily attacked.
if (params.isMember (jss::passphrase))
{
auto const entropy = estimate_entropy (
params[jss::passphrase].asString());
auto const passphrase = params[jss::passphrase].asString();
// 80 bits of entropy isn't bad, but it's better to
// err on the side of caution and be conservative.
if (entropy < 80.0)
obj[jss::warning] =
"This wallet was generated using a user-supplied "
"passphrase that has low entropy and is vulnerable "
"to brute-force attacks.";
else
obj[jss::warning] =
"This wallet was generated using a user-supplied "
"passphrase. It may be vulnerable to brute-force "
"attacks.";
if (passphrase != seed1751 &&
passphrase != seedBase58 &&
passphrase != seedHex)
{
// 80 bits of entropy isn't bad, but it's better to
// err on the side of caution and be conservative.
if (estimate_entropy (passphrase) < 80.0)
obj[jss::warning] =
"This wallet was generated using a user-supplied "
"passphrase that has low entropy and is vulnerable "
"to brute-force attacks.";
else
obj[jss::warning] =
"This wallet was generated using a user-supplied "
"passphrase. It may be vulnerable to brute-force "
"attacks.";
}
}
return obj;

View File

@@ -40,6 +40,8 @@ struct key_strings
char const* public_key;
char const* public_key_hex;
char const* secret_key_hex;
char const* passphrase;
char const* passphrase_warning;
};
namespace common {
@@ -58,6 +60,10 @@ static key_strings const secp256k1_strings =
"aBQxK2YFNqzmAaXNczYcjqDjfiKkLsJUizsr1UBf44RCF8FHdrmX",
"038AAE247B2344B1837FBED8F57389C8C11774510A3F7D784F2A09F0CB6843236C",
"1949ECD889EA71324BC7A30C8E81F4E93CB73EE19D59E9082111E78CC3DDABC2",
common::passphrase,
"This wallet was generated using a user-supplied "
"passphrase that has low entropy and is vulnerable "
"to brute-force attacks.",
};
static key_strings const ed25519_strings =
@@ -69,6 +75,25 @@ static key_strings const ed25519_strings =
"aKEQmgLMyZPMruJFejUuedp169LgW6DbJt1rej1DJ5hWUMH4pHJ7",
"ED54C3F5BEDA8BD588B203D23A27398FAD9D20F88A974007D6994659CD7273FE1D",
"77AAED2698D56D6676323629160F4EEF21CFD9EE3D0745CC78FA291461F98278",
common::passphrase,
"This wallet was generated using a user-supplied "
"passphrase that has low entropy and is vulnerable "
"to brute-force attacks.",
};
static key_strings const strong_brain_strings =
{
"rBcvXmNb7KPkNdMkpckdWPpbvkWgcV3nir",
"TED AVON CAVE HOUR BRAG JEFF RIFT NEAL TOLD FAT SEW SAN",
"shKdhWka8hS7Es3bpctCZXBiAwfUN",
"74BA8389B44F98CF41E795CD91F9C93F",
"aBRL2sqVuzrsM6zikPB4v8UBHGn1aKkrsxhYEffhcQxB2LKyywE5",
"03BD334FB9E06C58D69603E9922686528B18A754BC2F2E1ADA095FFE67DE952C64",
"84262FB16AA25BE407174C7EDAB531220C30FA4D8A28AA9D564673FB3D34502C",
"A4yKIRGdzrw0YQ$2%TFKYG9HP*&ok^!sy7E@RwICs",
"This wallet was generated using a user-supplied "
"passphrase. It may be vulnerable to brute-force "
"attacks.",
};
class WalletPropose_test : public ripple::TestSuite
@@ -93,6 +118,7 @@ public:
expectEquals (result[jss::key_type],
params.isMember (jss::key_type) ? params[jss::key_type]
: "secp256k1");
BEAST_EXPECT(!result.isMember(jss::warning));
std::string seed = result[jss::master_seed].asString();
@@ -102,7 +128,7 @@ public:
BEAST_EXPECT(result[jss::master_seed].asString() != seed);
}
void testSecretWallet (Json::Value const& params, key_strings const& s)
Json::Value testSecretWallet (Json::Value const& params, key_strings const& s)
{
Json::Value result = walletPropose (params);
@@ -116,6 +142,7 @@ public:
expectEquals (result[jss::key_type],
params.isMember (jss::key_type) ? params[jss::key_type]
: "secp256k1");
return result;
}
void testSeed (boost::optional<std::string> const& keyType,
@@ -128,7 +155,8 @@ public:
params[jss::key_type] = *keyType;
params[jss::seed] = strings.master_seed;
testSecretWallet (params, strings);
auto const wallet = testSecretWallet (params, strings);
BEAST_EXPECT(!wallet.isMember(jss::warning));
}
void testSeedHex (boost::optional<std::string> const& keyType,
@@ -141,7 +169,8 @@ public:
params[jss::key_type] = *keyType;
params[jss::seed_hex] = strings.master_seed_hex;
testSecretWallet (params, strings);
auto const wallet = testSecretWallet (params, strings);
BEAST_EXPECT(!wallet.isMember(jss::warning));
}
void testLegacyPassphrase (char const* value,
@@ -153,7 +182,11 @@ public:
params[jss::key_type] = *keyType;
params[jss::passphrase] = value;
testSecretWallet (params, strings);
auto const wallet = testSecretWallet (params, strings);
if (value == strings.passphrase)
BEAST_EXPECT(wallet[jss::warning] == strings.passphrase_warning);
else
BEAST_EXPECT(!wallet.isMember(jss::warning));
}
void testLegacyPassphrase(boost::optional<std::string> const& keyType,
@@ -161,7 +194,7 @@ public:
{
testcase ("passphrase");
testLegacyPassphrase (common::passphrase, keyType, strings);
testLegacyPassphrase (strings.passphrase, keyType, strings);
testLegacyPassphrase (strings.master_key, keyType, strings);
testLegacyPassphrase (strings.master_seed, keyType, strings);
testLegacyPassphrase (strings.master_seed_hex, keyType, strings);
@@ -682,11 +715,13 @@ public:
testKeyType (boost::none, secp256k1_strings);
testKeyType (std::string("secp256k1"), secp256k1_strings);
testKeyType (std::string("ed25519"), ed25519_strings);
testKeyType (std::string("secp256k1"), strong_brain_strings);
testBadInput ();
testKeypairForSignature (boost::none, secp256k1_strings);
testKeypairForSignature (std::string("secp256k1"), secp256k1_strings);
testKeypairForSignature (std::string("ed25519"), ed25519_strings);
testKeypairForSignature (std::string("secp256k1"), strong_brain_strings);
testKeypairForSignatureErrors ();
}
};