diff --git a/src/ripple/rpc/handlers/WalletPropose.cpp b/src/ripple/rpc/handlers/WalletPropose.cpp index 51a90e3ae..decea9799 100644 --- a/src/ripple/rpc/handlers/WalletPropose.cpp +++ b/src/ripple/rpc/handlers/WalletPropose.cpp @@ -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; diff --git a/src/test/rpc/KeyGeneration_test.cpp b/src/test/rpc/KeyGeneration_test.cpp index e9d3624e5..a16a07f0d 100644 --- a/src/test/rpc/KeyGeneration_test.cpp +++ b/src/test/rpc/KeyGeneration_test.cpp @@ -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 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 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 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 (); } };