std::string testPassphrase(std::string passphrase) { auto const seed1 = generateSeed (passphrase); auto const seed2 = parseBase58<Seed>(toBase58(seed1)); BEAST_EXPECT(static_cast<bool>(seed2)); BEAST_EXPECT(equal (seed1, *seed2)); return toBase58(seed1); }
bool ValidatorList::removePublisherList (PublicKey const& publisherKey) { auto const iList = publisherLists_.find (publisherKey); if (iList == publisherLists_.end ()) return false; JLOG (j_.debug()) << "Removing validator list for revoked publisher " << toBase58(TokenType::TOKEN_NODE_PUBLIC, publisherKey); for (auto const& val : iList->second.list) { auto const& iVal = keyListings_.find (val); if (iVal == keyListings_.end()) continue; if (iVal->second <= 1) keyListings_.erase (iVal); else --iVal->second; } iList->second.list.clear(); iList->second.available = false; return true; }
Account::Account(std::string name, std::pair<PublicKey, SecretKey> const& keys) : name_(std::move(name)) , pk_ (keys.first) , sk_ (keys.second) , id_ (calcAccountID(pk_)) , human_ (toBase58(id_)) { }
Account::Account (std::string name, KeyType type) : name_(std::move(name)) { auto const keys = generateKeyPair( type, generateSeed(name_)); pk_ = keys.first; sk_ = keys.second; id_ = calcAccountID(pk_); human_ = toBase58(id_); }
void testSeedParsing () { testcase ("Parsing"); // account IDs and node and account public and private // keys should not be parseable as seeds. auto const node1 = randomKeyPair(KeyType::secp256k1); BEAST_EXPECT(!parseGenericSeed ( toBase58 (TokenType::TOKEN_NODE_PUBLIC, node1.first))); BEAST_EXPECT(!parseGenericSeed ( toBase58 (TokenType::TOKEN_NODE_PRIVATE, node1.second))); auto const node2 = randomKeyPair(KeyType::ed25519); BEAST_EXPECT(!parseGenericSeed ( toBase58 (TokenType::TOKEN_NODE_PUBLIC, node2.first))); BEAST_EXPECT(!parseGenericSeed ( toBase58 (TokenType::TOKEN_NODE_PRIVATE, node2.second))); auto const account1 = generateKeyPair( KeyType::secp256k1, randomSeed ()); BEAST_EXPECT(!parseGenericSeed ( toBase58(calcAccountID(account1.first)))); BEAST_EXPECT(!parseGenericSeed ( toBase58(TokenType::TOKEN_ACCOUNT_PUBLIC, account1.first))); BEAST_EXPECT(!parseGenericSeed ( toBase58(TokenType::TOKEN_ACCOUNT_SECRET, account1.second))); auto const account2 = generateKeyPair( KeyType::ed25519, randomSeed ()); BEAST_EXPECT(!parseGenericSeed ( toBase58(calcAccountID(account2.first)))); BEAST_EXPECT(!parseGenericSeed ( toBase58(TokenType::TOKEN_ACCOUNT_PUBLIC, account2.first))); BEAST_EXPECT(!parseGenericSeed ( toBase58(TokenType::TOKEN_ACCOUNT_SECRET, account2.second))); }
Stream& logMftAct ( Stream& s, std::string const& action, PublicKey const& pk, std::uint32_t seq) { s << "Manifest: " << action << ";Pk: " << toBase58 (TokenType::TOKEN_NODE_PUBLIC, pk) << ";Seq: " << seq << ";"; return s; }
TER SetSignerList::replaceSignerList () { auto const accountKeylet = keylet::account (account_); auto const ownerDirKeylet = keylet::ownerDir (account_); auto const signerListKeylet = keylet::signers (account_); // This may be either a create or a replace. Preemptively remove any // old signer list. May reduce the reserve, so this is done before // checking the reserve. if (TER const ter = removeSignersFromLedger ( accountKeylet, ownerDirKeylet, signerListKeylet)) return ter; auto const sle = view().peek(accountKeylet); // Compute new reserve. Verify the account has funds to meet the reserve. auto const oldOwnerCount = (*sle)[sfOwnerCount]; std::uint32_t const addedOwnerCount = ownerCountDelta (signers_.size ()); auto const newReserve = view().fees().accountReserve(oldOwnerCount + addedOwnerCount); // We check the reserve against the starting balance because we want to // allow dipping into the reserve to pay fees. This behavior is consistent // with CreateTicket. if (mPriorBalance < newReserve) return tecINSUFFICIENT_RESERVE; // Everything's ducky. Add the ltSIGNER_LIST to the ledger. auto signerList = std::make_shared<SLE>(signerListKeylet); view().insert (signerList); writeSignersToSLE (signerList); auto viewJ = ctx_.app.journal ("View"); // Add the signer list to the account's directory. std::uint64_t hint; TER result = dirAdd(ctx_.view (), hint, ownerDirKeylet.key, signerListKeylet.key, describeOwnerDir (account_), viewJ); JLOG(j_.trace) << "Create signer list for account " << toBase58(account_) << ": " << transHuman (result); if (result != tesSUCCESS) return result; signerList->setFieldU64 (sfOwnerNode, hint); // If we succeeded, the new entry counts against the creator's reserve. adjustOwnerCount(view(), sle, addedOwnerCount, viewJ); return result; }
void testRandom() { testcase ("random generation"); for (int i = 0; i < 32; i++) { auto const seed1 = randomSeed (); auto const seed2 = parseBase58<Seed>(toBase58(seed1)); BEAST_EXPECT(static_cast<bool>(seed2)); BEAST_EXPECT(equal (seed1, *seed2)); } }
// { // secret: <string> // optional // } // // This command requires Role::ADMIN access because it makes // no sense to ask an untrusted server for this. Json::Value doValidationCreate (RPC::Context& context) { Json::Value obj (Json::objectValue); auto seed = validationSeed(context.params); if (!seed) return rpcError (rpcBAD_SEED); auto const private_key = generateSecretKey (KeyType::secp256k1, *seed); obj[jss::validation_public_key] = toBase58 ( TokenType::TOKEN_NODE_PUBLIC, derivePublicKey (KeyType::secp256k1, private_key)); obj[jss::validation_private_key] = toBase58 ( TokenType::TOKEN_NODE_PRIVATE, private_key); obj[jss::validation_seed] = toBase58 (*seed); obj[jss::validation_key] = seedAs1751 (*seed); return obj; }
// VFALCO This could be a free function elsewhere std::string STTx::getMetaSQL (Serializer rawTxn, std::uint32_t inLedger, char status, std::string const& escapedMetaData) const { static boost::format bfTrans ("('%s', '%s', '%s', '%d', '%d', '%c', %s, %s)"); std::string rTxn = sqlEscape (rawTxn.peekData ()); auto format = TxFormats::getInstance().findByType (tx_type_); assert (format != nullptr); return str (boost::format (bfTrans) % to_string (getTransactionID ()) % format->getName () % toBase58(getAccountID(sfAccount)) % getSequence () % inLedger % status % rTxn % escapedMetaData); }
NotTEC Transactor::checkSeq (PreclaimContext const& ctx) { auto const id = ctx.tx.getAccountID(sfAccount); auto const sle = ctx.view.read( keylet::account(id)); if (!sle) { JLOG(ctx.j.trace()) << "applyTransaction: delay: source account does not exist " << toBase58(ctx.tx.getAccountID(sfAccount)); return terNO_ACCOUNT; } std::uint32_t const t_seq = ctx.tx.getSequence (); std::uint32_t const a_seq = sle->getFieldU32 (sfSequence); if (t_seq != a_seq) { if (a_seq < t_seq) { JLOG(ctx.j.trace()) << "applyTransaction: has future sequence number " << "a_seq=" << a_seq << " t_seq=" << t_seq; return terPRE_SEQ; } if (ctx.view.txExists(ctx.tx.getTransactionID ())) return tefALREADY; JLOG(ctx.j.trace()) << "applyTransaction: has past sequence number " << "a_seq=" << a_seq << " t_seq=" << t_seq; return tefPAST_SEQ; } if (ctx.tx.isFieldPresent (sfAccountTxnID) && (sle->getFieldH256 (sfAccountTxnID) != ctx.tx.getFieldH256 (sfAccountTxnID))) return tefWRONG_PRIOR; if (ctx.tx.isFieldPresent (sfLastLedgerSequence) && (ctx.view.seq() > ctx.tx.getFieldU32 (sfLastLedgerSequence))) return tefMAX_LEDGER; return tesSUCCESS; }
Json::Value PathRequest::doCreate ( RippleLineCache::ref& cache, Json::Value const& value, bool& valid) { Json::Value status; if (parseJson (value, true) != PFR_PJ_INVALID) { bValid = isValid (cache); if (bValid) status = doUpdate (cache, true); else status = jvStatus; } else { bValid = false; status = jvStatus; } if (m_journal.debug) { if (bValid) { m_journal.debug << iIdentifier << " valid: " << toBase58(*raSrcAccount); m_journal.debug << iIdentifier << " Deliver: " << saDstAmount.getFullText (); } else { m_journal.debug << iIdentifier << " invalid"; } } valid = bValid; return status; }
void addChannel (Json::Value& jsonLines, SLE const& line) { Json::Value& jDst (jsonLines.append (Json::objectValue)); jDst[jss::channel_id] = to_string (line.key ()); jDst[jss::account] = to_string (line[sfAccount]); jDst[jss::destination_account] = to_string (line[sfDestination]); jDst[jss::amount] = line[sfAmount].getText (); jDst[jss::balance] = line[sfBalance].getText (); PublicKey const pk (line[sfPublicKey]); jDst[jss::public_key] = toBase58 (TokenType::TOKEN_ACCOUNT_PUBLIC, pk); jDst[jss::public_key_hex] = strHex (pk); jDst[jss::settle_delay] = line[sfSettleDelay]; if (auto const& v = line[~sfExpiration]) jDst[jss::expiration] = *v; if (auto const& v = line[~sfCancelAfter]) jDst[jss::cancel_after] = *v; if (auto const& v = line[~sfSourceTag]) jDst[jss::source_tag] = *v; if (auto const& v = line[~sfDestinationTag]) jDst[jss::destination_tag] = *v; }
Json::Value doUnlList (RPC::Context& context) { auto lock = make_lock(context.app.getMasterMutex()); Json::Value obj (Json::objectValue); context.app.validators().for_each_listed ( [&unl = obj[jss::unl]]( PublicKey const& publicKey, bool trusted) { Json::Value node (Json::objectValue); node[jss::pubkey_validator] = toBase58( TokenType::TOKEN_NODE_PUBLIC, publicKey); node[jss::trusted] = trusted; unl.append (node); }); return obj; }
std::pair<bool, std::string> STTx::checkMultiSign () const { // Make sure the MultiSigners are present. Otherwise they are not // attempting multi-signing and we just have a bad SigningPubKey. if (!isFieldPresent (sfSigners)) return {false, "Empty SigningPubKey."}; // We don't allow both an sfSigners and an sfTxnSignature. Both fields // being present would indicate that the transaction is signed both ways. if (isFieldPresent (sfTxnSignature)) return {false, "Cannot both single- and multi-sign."}; STArray const& signers {getFieldArray (sfSigners)}; // There are well known bounds that the number of signers must be within. if (signers.size() < minMultiSigners || signers.size() > maxMultiSigners) return {false, "Invalid Signers array size."}; // We can ease the computational load inside the loop a bit by // pre-constructing part of the data that we hash. Fill a Serializer // with the stuff that stays constant from signature to signature. Serializer const dataStart {startMultiSigningData (*this)}; // We also use the sfAccount field inside the loop. Get it once. auto const txnAccountID = getAccountID (sfAccount); // Determine whether signatures must be full canonical. bool const fullyCanonical = (getFlags() & tfFullyCanonicalSig); // Signers must be in sorted order by AccountID. AccountID lastAccountID (beast::zero); for (auto const& signer : signers) { auto const accountID = signer.getAccountID (sfAccount); // The account owner may not multisign for themselves. if (accountID == txnAccountID) return {false, "Invalid multisigner."}; // No duplicate signers allowed. if (lastAccountID == accountID) return {false, "Duplicate Signers not allowed."}; // Accounts must be in order by account ID. No duplicates allowed. if (lastAccountID > accountID) return {false, "Unsorted Signers array."}; // The next signature must be greater than this one. lastAccountID = accountID; // Verify the signature. bool validSig = false; try { Serializer s = dataStart; finishMultiSigningData (accountID, s); auto spk = signer.getFieldVL (sfSigningPubKey); if (publicKeyType (makeSlice(spk))) { Blob const signature = signer.getFieldVL (sfTxnSignature); validSig = verify ( PublicKey (makeSlice(spk)), s.slice(), makeSlice(signature), fullyCanonical); } } catch (std::exception const&) { // We assume any problem lies with the signature. validSig = false; } if (!validSig) return {false, std::string("Invalid signature on account ") + toBase58(accountID) + "."}; } // All signatures verified. return {true, ""}; }
void ConnectAttempt::processResponse() { if (response_.result() == boost::beast::http::status::service_unavailable) { Json::Value json; Json::Reader r; std::string s; s.reserve(boost::asio::buffer_size(response_.body().data())); for(auto const& buffer : response_.body().data()) s.append( boost::asio::buffer_cast<char const*>(buffer), boost::asio::buffer_size(buffer)); auto const success = r.parse(s, json); if (success) { if (json.isObject() && json.isMember("peer-ips")) { Json::Value const& ips = json["peer-ips"]; if (ips.isArray()) { std::vector<boost::asio::ip::tcp::endpoint> eps; eps.reserve(ips.size()); for (auto const& v : ips) { if (v.isString()) { error_code ec; auto const ep = parse_endpoint(v.asString(), ec); if (!ec) eps.push_back(ep); } } overlay_.peerFinder().onRedirects( remote_endpoint_, eps); } } } } if (! OverlayImpl::isPeerUpgrade(response_)) { JLOG(journal_.info()) << "HTTP Response: " << response_.result() << " " << response_.reason(); return close(); } auto hello = parseHello (false, response_, journal_); if(! hello) return fail("processResponse: Bad TMHello"); auto sharedValue = makeSharedValue( ssl_bundle_->stream.native_handle(), journal_); if(! sharedValue) return close(); // makeSharedValue logs auto publicKey = verifyHello (*hello, *sharedValue, overlay_.setup().public_ip, beast::IPAddressConversion::from_asio(remote_endpoint_), journal_, app_); if(! publicKey) return close(); // verifyHello logs JLOG(journal_.info()) << "Public Key: " << toBase58 ( TokenType::NodePublic, *publicKey); auto const protocol = BuildInfo::make_protocol(hello->protoversion()); JLOG(journal_.info()) << "Protocol: " << to_string(protocol); auto member = app_.cluster().member(*publicKey); if (member) { JLOG(journal_.info()) << "Cluster name: " << *member; } auto const result = overlay_.peerFinder().activate (slot_, *publicKey, static_cast<bool>(member)); if (result != PeerFinder::Result::success) return fail("Outbound slots full"); auto const peer = std::make_shared<PeerImp>(app_, std::move(ssl_bundle_), read_buf_.data(), std::move(slot_), std::move(response_), usage_, *hello, *publicKey, id_, overlay_); overlay_.add_active (peer); }
Json::Value ValidatorList::getJson() const { Json::Value res(Json::objectValue); boost::shared_lock<boost::shared_mutex> read_lock{mutex_}; res[jss::validation_quorum] = static_cast<Json::UInt>(quorum()); if (auto when = expires()) { if (*when == TimeKeeper::time_point::max()) res[jss::validator_list_expires] = "never"; else res[jss::validator_list_expires] = to_string(*when); } else res[jss::validator_list_expires] = "unknown"; // Local static keys PublicKey local; Json::Value& jLocalStaticKeys = (res[jss::local_static_keys] = Json::arrayValue); auto it = publisherLists_.find(local); if (it != publisherLists_.end()) { for (auto const& key : it->second.list) jLocalStaticKeys.append( toBase58(TokenType::TOKEN_NODE_PUBLIC, key)); } // Publisher lists Json::Value& jPublisherLists = (res[jss::publisher_lists] = Json::arrayValue); for (auto const& p : publisherLists_) { if(local == p.first) continue; Json::Value& curr = jPublisherLists.append(Json::objectValue); curr[jss::pubkey_publisher] = strHex(p.first); curr[jss::available] = p.second.available; if(p.second.expiration != TimeKeeper::time_point{}) { curr[jss::seq] = static_cast<Json::UInt>(p.second.sequence); curr[jss::expiration] = to_string(p.second.expiration); curr[jss::version] = requiredListVersion; } Json::Value& keys = (curr[jss::list] = Json::arrayValue); for (auto const& key : p.second.list) { keys.append(toBase58(TokenType::TOKEN_NODE_PUBLIC, key)); } } // Trusted validator keys Json::Value& jValidatorKeys = (res[jss::trusted_validator_keys] = Json::arrayValue); for (auto const& k : trustedKeys_) { jValidatorKeys.append(toBase58(TokenType::TOKEN_NODE_PUBLIC, k)); } // signing keys Json::Value& jSigningKeys = (res[jss::signing_keys] = Json::objectValue); validatorManifests_.for_each_manifest( [&jSigningKeys, this](Manifest const& manifest) { auto it = keyListings_.find(manifest.masterKey); if (it != keyListings_.end()) { jSigningKeys[toBase58( TokenType::TOKEN_NODE_PUBLIC, manifest.masterKey)] = toBase58(TokenType::TOKEN_NODE_PUBLIC, manifest.signingKey); } }); return res; }
std::string STAccount::getText () const { if (isDefault()) return ""; return toBase58 (value()); }
Json::Value walletPropose (Json::Value const& params) { boost::optional<KeyType> keyType; boost::optional<Seed> seed; bool rippleLibSeed = false; if (params.isMember (jss::key_type)) { if (! params[jss::key_type].isString()) { return RPC::expected_field_error ( jss::key_type, "string"); } keyType = keyTypeFromString ( params[jss::key_type].asString()); if (!keyType) return rpcError(rpcINVALID_PARAMS); } // ripple-lib encodes seed used to generate an Ed25519 wallet in a // non-standard way. While we never encode seeds that way, we try // to detect such keys to avoid user confusion. { if (params.isMember(jss::passphrase)) seed = RPC::parseRippleLibSeed(params[jss::passphrase]); else if (params.isMember(jss::seed)) seed = RPC::parseRippleLibSeed(params[jss::seed]); if(seed) { rippleLibSeed = true; // If the user *explicitly* requests a key type other than // Ed25519 we return an error. if (keyType.value_or(KeyType::ed25519) != KeyType::ed25519) return rpcError(rpcBAD_SEED); keyType = KeyType::ed25519; } } if (!seed) { if (params.isMember(jss::passphrase) || params.isMember(jss::seed) || params.isMember(jss::seed_hex)) { Json::Value err; seed = RPC::getSeedFromRPC(params, err); if (!seed) return err; } else { seed = randomSeed(); } } if (!keyType) keyType = KeyType::secp256k1; auto const publicKey = generateKeyPair (*keyType, *seed).first; Json::Value obj (Json::objectValue); auto const seed1751 = seedAs1751 (*seed); auto const seedHex = strHex (*seed); 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(TokenType::AccountPublic, publicKey); obj[jss::key_type] = to_string (*keyType); obj[jss::public_key_hex] = strHex (publicKey); // 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 (!rippleLibSeed && params.isMember (jss::passphrase)) { auto const passphrase = params[jss::passphrase].asString(); 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; }
void testKeypairGenerationAndSigning () { std::string const message1 = "http://www.ripple.com"; std::string const message2 = "https://www.ripple.com"; { testcase ("Node keypair generation & signing (secp256k1)"); auto const secretKey = generateSecretKey ( KeyType::secp256k1, generateSeed ("masterpassphrase")); auto const publicKey = derivePublicKey ( KeyType::secp256k1, secretKey); BEAST_EXPECT(toBase58(TokenType::TOKEN_NODE_PUBLIC, publicKey) == "n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9"); BEAST_EXPECT(toBase58(TokenType::TOKEN_NODE_PRIVATE, secretKey) == "pnen77YEeUd4fFKG7iycBWcwKpTaeFRkW2WFostaATy1DSupwXe"); BEAST_EXPECT(to_string(calcNodeID(publicKey)) == "7E59C17D50F5959C7B158FEC95C8F815BF653DC8"); auto sig = sign (publicKey, secretKey, makeSlice(message1)); BEAST_EXPECT(sig.size() != 0); BEAST_EXPECT(verify (publicKey, makeSlice(message1), sig)); // Correct public key but wrong message BEAST_EXPECT(!verify (publicKey, makeSlice(message2), sig)); // Verify with incorrect public key { auto const otherPublicKey = derivePublicKey ( KeyType::secp256k1, generateSecretKey ( KeyType::secp256k1, generateSeed ("otherpassphrase"))); BEAST_EXPECT(!verify (otherPublicKey, makeSlice(message1), sig)); } // Correct public key but wrong signature { // Slightly change the signature: if (auto ptr = sig.data()) ptr[sig.size() / 2]++; BEAST_EXPECT(!verify (publicKey, makeSlice(message1), sig)); } } { testcase ("Node keypair generation & signing (ed25519)"); auto const secretKey = generateSecretKey ( KeyType::ed25519, generateSeed ("masterpassphrase")); auto const publicKey = derivePublicKey ( KeyType::ed25519, secretKey); BEAST_EXPECT(toBase58(TokenType::TOKEN_NODE_PUBLIC, publicKey) == "nHUeeJCSY2dM71oxM8Cgjouf5ekTuev2mwDpc374aLMxzDLXNmjf"); BEAST_EXPECT(toBase58(TokenType::TOKEN_NODE_PRIVATE, secretKey) == "paKv46LztLqK3GaKz1rG2nQGN6M4JLyRtxFBYFTw4wAVHtGys36"); BEAST_EXPECT(to_string(calcNodeID(publicKey)) == "AA066C988C712815CC37AF71472B7CBBBD4E2A0A"); auto sig = sign (publicKey, secretKey, makeSlice(message1)); BEAST_EXPECT(sig.size() != 0); BEAST_EXPECT(verify (publicKey, makeSlice(message1), sig)); // Correct public key but wrong message BEAST_EXPECT(!verify (publicKey, makeSlice(message2), sig)); // Verify with incorrect public key { auto const otherPublicKey = derivePublicKey ( KeyType::ed25519, generateSecretKey ( KeyType::ed25519, generateSeed ("otherpassphrase"))); BEAST_EXPECT(!verify (otherPublicKey, makeSlice(message1), sig)); } // Correct public key but wrong signature { // Slightly change the signature: if (auto ptr = sig.data()) ptr[sig.size() / 2]++; BEAST_EXPECT(!verify (publicKey, makeSlice(message1), sig)); } } { testcase ("Account keypair generation & signing (secp256k1)"); auto const keyPair = generateKeyPair ( KeyType::secp256k1, generateSeed ("masterpassphrase")); BEAST_EXPECT(toBase58(calcAccountID(keyPair.first)) == "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"); BEAST_EXPECT(toBase58(TokenType::TOKEN_ACCOUNT_PUBLIC, keyPair.first) == "aBQG8RQAzjs1eTKFEAQXr2gS4utcDiEC9wmi7pfUPTi27VCahwgw"); BEAST_EXPECT(toBase58(TokenType::TOKEN_ACCOUNT_SECRET, keyPair.second) == "p9JfM6HHi64m6mvB6v5k7G2b1cXzGmYiCNJf6GHPKvFTWdeRVjh"); auto sig = sign (keyPair.first, keyPair.second, makeSlice(message1)); BEAST_EXPECT(sig.size() != 0); BEAST_EXPECT(verify (keyPair.first, makeSlice(message1), sig)); // Correct public key but wrong message BEAST_EXPECT(!verify (keyPair.first, makeSlice(message2), sig)); // Verify with incorrect public key { auto const otherKeyPair = generateKeyPair ( KeyType::secp256k1, generateSeed ("otherpassphrase")); BEAST_EXPECT(!verify (otherKeyPair.first, makeSlice(message1), sig)); } // Correct public key but wrong signature { // Slightly change the signature: if (auto ptr = sig.data()) ptr[sig.size() / 2]++; BEAST_EXPECT(!verify (keyPair.first, makeSlice(message1), sig)); } } { testcase ("Account keypair generation & signing (ed25519)"); auto const keyPair = generateKeyPair ( KeyType::ed25519, generateSeed ("masterpassphrase")); BEAST_EXPECT(to_string(calcAccountID(keyPair.first)) == "rGWrZyQqhTp9Xu7G5Pkayo7bXjH4k4QYpf"); BEAST_EXPECT(toBase58(TokenType::TOKEN_ACCOUNT_PUBLIC, keyPair.first) == "aKGheSBjmCsKJVuLNKRAKpZXT6wpk2FCuEZAXJupXgdAxX5THCqR"); BEAST_EXPECT(toBase58(TokenType::TOKEN_ACCOUNT_SECRET, keyPair.second) == "pwDQjwEhbUBmPuEjFpEG75bFhv2obkCB7NxQsfFxM7xGHBMVPu9"); auto sig = sign (keyPair.first, keyPair.second, makeSlice(message1)); BEAST_EXPECT(sig.size() != 0); BEAST_EXPECT(verify (keyPair.first, makeSlice(message1), sig)); // Correct public key but wrong message BEAST_EXPECT(!verify (keyPair.first, makeSlice(message2), sig)); // Verify with incorrect public key { auto const otherKeyPair = generateKeyPair ( KeyType::ed25519, generateSeed ("otherpassphrase")); BEAST_EXPECT(!verify (otherKeyPair.first, makeSlice(message1), sig)); } // Correct public key but wrong signature { // Slightly change the signature: if (auto ptr = sig.data()) ptr[sig.size() / 2]++; BEAST_EXPECT(!verify (keyPair.first, makeSlice(message1), sig)); } } }
void run() { // Construct a seed. RippleAddress naSeed; expect (naSeed.setSeedGeneric ("masterpassphrase")); expect (naSeed.humanSeed () == "snoPBrXtMeMyMHUVTgbuqAfg1SUTb", naSeed.humanSeed ()); // Create node public/private key pair RippleAddress naNodePublic = RippleAddress::createNodePublic (naSeed); RippleAddress naNodePrivate = RippleAddress::createNodePrivate (naSeed); expect (naNodePublic.humanNodePublic () == "n94a1u4jAz288pZLtw6yFWVbi89YamiC6JBXPVUj5zmExe5fTVg9", naNodePublic.humanNodePublic ()); expect (naNodePrivate.humanNodePrivate () == "pnen77YEeUd4fFKG7iycBWcwKpTaeFRkW2WFostaATy1DSupwXe", naNodePrivate.humanNodePrivate ()); // Check node signing. Blob vucTextSrc = strCopy ("Hello, nurse!"); uint256 uHash = sha512Half(makeSlice(vucTextSrc)); Blob vucTextSig; naNodePrivate.signNodePrivate (uHash, vucTextSig); expect (naNodePublic.verifyNodePublic (uHash, vucTextSig, ECDSA::strict), "Verify failed."); // Construct a public generator from the seed. RippleAddress generator = RippleAddress::createGeneratorPublic (naSeed); expect (generator.humanGenerator () == "fhuJKrhSDzV2SkjLn9qbwm5AaRmrxDPfFsHDCP6yfDZWcxDFz4mt", generator.humanGenerator ()); // Create ed25519 account public/private key pair. KeyPair keys = generateKeysFromSeed (KeyType::ed25519, naSeed); expectEquals (keys.publicKey.humanAccountPublic(), "aKGheSBjmCsKJVuLNKRAKpZXT6wpk2FCuEZAXJupXgdAxX5THCqR"); // Check ed25519 account signing. vucTextSig = keys.secretKey.accountPrivateSign (vucTextSrc); expect (!vucTextSig.empty(), "ed25519 signing failed."); expect (keys.publicKey.accountPublicVerify (vucTextSrc, vucTextSig, ECDSA()), "ed25519 verify failed."); // Create account #0 public/private key pair. RippleAddress naAccountPublic0 = RippleAddress::createAccountPublic (generator, 0); RippleAddress naAccountPrivate0 = RippleAddress::createAccountPrivate (generator, naSeed, 0); expect (toBase58(calcAccountID(naAccountPublic0)) == "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh"); expect (naAccountPublic0.humanAccountPublic () == "aBQG8RQAzjs1eTKFEAQXr2gS4utcDiEC9wmi7pfUPTi27VCahwgw", naAccountPublic0.humanAccountPublic ()); // Create account #1 public/private key pair. RippleAddress naAccountPublic1 = RippleAddress::createAccountPublic (generator, 1); RippleAddress naAccountPrivate1 = RippleAddress::createAccountPrivate (generator, naSeed, 1); expect (toBase58(calcAccountID(naAccountPublic1)) == "r4bYF7SLUMD7QgSLLpgJx38WJSY12ViRjP"); expect (naAccountPublic1.humanAccountPublic () == "aBPXpTfuLy1Bhk3HnGTTAqnovpKWQ23NpFMNkAF6F1Atg5vDyPrw", naAccountPublic1.humanAccountPublic ()); // Check account signing. vucTextSig = naAccountPrivate0.accountPrivateSign (vucTextSrc); expect (!vucTextSig.empty(), "Signing failed."); expect (naAccountPublic0.accountPublicVerify (vucTextSrc, vucTextSig, ECDSA::strict), "Verify failed."); expect (!naAccountPublic1.accountPublicVerify (vucTextSrc, vucTextSig, ECDSA::not_strict), "Anti-verify failed."); expect (!naAccountPublic1.accountPublicVerify (vucTextSrc, vucTextSig, ECDSA::strict), "Anti-verify failed."); vucTextSig = naAccountPrivate1.accountPrivateSign (vucTextSrc); expect (!vucTextSig.empty(), "Signing failed."); expect (naAccountPublic1.accountPublicVerify (vucTextSrc, vucTextSig, ECDSA::strict), "Verify failed."); expect (!naAccountPublic0.accountPublicVerify (vucTextSrc, vucTextSig, ECDSA::not_strict), "Anti-verify failed."); expect (!naAccountPublic0.accountPublicVerify (vucTextSrc, vucTextSig, ECDSA::strict), "Anti-verify failed."); // Check account encryption. Blob vucTextCipher = naAccountPrivate0.accountPrivateEncrypt (naAccountPublic1, vucTextSrc); Blob vucTextRecovered = naAccountPrivate1.accountPrivateDecrypt (naAccountPublic0, vucTextCipher); expect (vucTextSrc == vucTextRecovered, "Encrypt-decrypt failed."); { RippleAddress nSeed; uint128 seed1, seed2; seed1.SetHex ("71ED064155FFADFA38782C5E0158CB26"); nSeed.setSeed (seed1); expect (nSeed.humanSeed() == "shHM53KPZ87Gwdqarm1bAmPeXg8Tn", "Incorrect human seed"); expect (nSeed.humanSeed1751() == "MAD BODY ACE MINT OKAY HUB WHAT DATA SACK FLAT DANA MATH", "Incorrect 1751 seed"); } }
Json::Value walletPropose (Json::Value const& params) { boost::optional<Seed> seed; KeyType keyType = KeyType::secp256k1; if (params.isMember (jss::key_type)) { if (! params[jss::key_type].isString()) { return RPC::expected_field_error ( jss::key_type, "string"); } keyType = keyTypeFromString ( params[jss::key_type].asString()); if (keyType == KeyType::invalid) return rpcError(rpcINVALID_PARAMS); } if (params.isMember (jss::passphrase) || params.isMember (jss::seed) || params.isMember (jss::seed_hex)) { Json::Value err; seed = RPC::getSeedFromRPC (params, err); if (!seed) return err; } else { seed = randomSeed (); } auto const publicKey = generateKeyPair (keyType, *seed).first; 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); 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 (params.isMember (jss::passphrase)) { auto const entropy = estimate_entropy ( 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."; } return obj; }