PublicKey::PublicKey (Slice const& slice) { if(! publicKeyType(slice)) LogicError("PublicKey::PublicKey invalid type"); size_ = slice.size(); std::memcpy(buf_, slice.data(), size_); }
void msig::operator()(Env& env, JTx& jt) const { auto const mySigners = signers; jt.signer = [mySigners, &env](Env&, JTx& jt) { jt[sfSigningPubKey.getJsonName()] = ""; boost::optional<STObject> st; try { st = parse(jt.jv); } catch(parse_error const&) { env.test.log << pretty(jt.jv) << std::endl; Rethrow(); } auto& js = jt[sfSigners.getJsonName()]; js.resize(mySigners.size()); for(std::size_t i = 0; i < mySigners.size(); ++i) { auto const& e = mySigners[i]; auto& jo = js[i][sfSigner.getJsonName()]; jo[jss::Account] = e.acct.human(); jo[jss::SigningPubKey] = strHex(e.sig.pk().slice()); Serializer ss {buildMultiSigningData (*st, e.acct.id())}; auto const sig = ripple::sign ( *publicKeyType(e.sig.pk().slice()), e.sig.sk(), ss.slice()); jo[sfTxnSignature.getJsonName()] = strHex(Slice{ sig.data(), sig.size() }); } }; }
/** Performs early sanity checks on the account and fee fields */ NotTEC preflight1 (PreflightContext const& ctx) { auto const ret = preflight0(ctx); if (!isTesSuccess(ret)) return ret; auto const id = ctx.tx.getAccountID(sfAccount); if (id == beast::zero) { JLOG(ctx.j.warn()) << "preflight1: bad account id"; return temBAD_SRC_ACCOUNT; } // No point in going any further if the transaction fee is malformed. auto const fee = ctx.tx.getFieldAmount (sfFee); if (!fee.native () || fee.negative () || !isLegalAmount (fee.xrp ())) { JLOG(ctx.j.debug()) << "preflight1: invalid fee"; return temBAD_FEE; } auto const spk = ctx.tx.getSigningPubKey(); if (!spk.empty () && !publicKeyType (makeSlice (spk))) { JLOG(ctx.j.debug()) << "preflight1: invalid signing key"; return temBAD_SIGNATURE; } return tesSUCCESS; }
bool verify (PublicKey const& publicKey, Slice const& m, Slice const& sig, bool mustBeFullyCanonical) { if (auto const type = publicKeyType(publicKey)) { if (*type == KeyType::secp256k1) { return verifyDigest (publicKey, sha512Half(m), sig, mustBeFullyCanonical); } else if (*type == KeyType::ed25519) { if (! ed25519Canonical(sig)) return false; // We internally prefix Ed25519 keys with a 0xED // byte to distinguish them from secp256k1 keys // so when verifying the signature, we need to // first strip that prefix. return ed25519_sign_open( m.data(), m.size(), publicKey.data() + 1, sig.data()) == 0; } } return false; }
boost::optional<PublicKey> parseBase58 (TokenType type, std::string const& s) { auto const result = decodeBase58Token(s, type); auto const pks = makeSlice(result); if (!publicKeyType(pks)) return boost::none; return PublicKey(pks); }
void msig::operator()(Env const& env, JTx& jt) const { // VFALCO Inefficient pre-C++14 auto accounts = accounts_; std::sort(accounts.begin(), accounts.end(), [](Account const& lhs, Account const& rhs) { return lhs.id() < rhs.id(); }); jt.signer = [accounts, &env](Env&, JTx& jt) { jt["SigningPubKey"] = ""; boost::optional<STObject> st; try { st = parse(jt.jv); } catch(parse_error const&) { env.test.log << pretty(jt.jv); throw; } auto const signFor = parseBase58<AccountID>( jt.jv[jss::Account].asString()); if (! signFor) { env.test.log << "invalid AccountID: '" << jt.jv[jss::Account].asString() << "'"; throw parse_error("msig: bad Account"); } auto& jv = jt["MultiSigners"][0u]["SigningFor"]; jv[jss::Account] = jt[jss::Account]; auto& js = jv["SigningAccounts"]; js.resize(accounts.size()); for(std::size_t i = 0; i < accounts.size(); ++i) { auto const& e = accounts[i]; auto& jo = js[i]["SigningAccount"]; jo[jss::Account] = e.human(); jo[jss::SigningPubKey] = strHex(e.pk().slice()); Serializer ss; ss.add32 (HashPrefix::txMultiSign); st->addWithoutSigningFields(ss); ss.add160(*signFor); ss.add160(e.id()); auto const sig = ripple::sign( *publicKeyType(e.pk().slice()), e.sk(), ss.slice()); jo["MultiSignature"] = strHex(Slice{ sig.data(), sig.size() }); } }; }
bool verifyDigest (PublicKey const& publicKey, uint256 const& digest, Slice const& sig, bool mustBeFullyCanonical) { if (publicKeyType(publicKey) != KeyType::secp256k1) LogicError("sign: secp256k1 required for digest signing"); auto const canonicality = ecdsaCanonicality(sig); if (! canonicality) return false; if (mustBeFullyCanonical && (*canonicality != ECDSACanonicality::fullyCanonical)) return false; secp256k1_pubkey pubkey_imp; if(secp256k1_ec_pubkey_parse( secp256k1Context(), &pubkey_imp, reinterpret_cast<unsigned char const*>( publicKey.data()), publicKey.size()) != 1) return false; secp256k1_ecdsa_signature sig_imp; if(secp256k1_ecdsa_signature_parse_der( secp256k1Context(), &sig_imp, reinterpret_cast<unsigned char const*>( sig.data()), sig.size()) != 1) return false; if (*canonicality != ECDSACanonicality::fullyCanonical) { secp256k1_ecdsa_signature sig_norm; if(secp256k1_ecdsa_signature_normalize( secp256k1Context(), &sig_norm, &sig_imp) != 1) return false; return secp256k1_ecdsa_verify( secp256k1Context(), &sig_norm, reinterpret_cast<unsigned char const*>( digest.data()), &pubkey_imp) == 1; } return secp256k1_ecdsa_verify( secp256k1Context(), &sig_imp, reinterpret_cast<unsigned char const*>( digest.data()), &pubkey_imp) == 1; }
boost::optional<Manifest> Manifest::make_Manifest (std::string s) { try { STObject st (sfGeneric); SerialIter sit (s.data (), s.size ()); st.set (sit); auto const pk = st.getFieldVL (sfPublicKey); if (! publicKeyType (makeSlice(pk))) return boost::none; auto const opt_seq = get (st, sfSequence); auto const opt_msig = get (st, sfMasterSignature); if (!opt_seq || !opt_msig) return boost::none; // Signing key and signature are not required for // master key revocations if (*opt_seq != std::numeric_limits<std::uint32_t>::max ()) { auto const spk = st.getFieldVL (sfSigningPubKey); if (! publicKeyType (makeSlice(spk))) return boost::none; auto const opt_sig = get (st, sfSignature); if (! opt_sig) return boost::none; return Manifest (std::move (s), PublicKey (makeSlice(pk)), PublicKey (makeSlice(spk)), *opt_seq); } return Manifest (std::move (s), PublicKey (makeSlice(pk)), PublicKey(), *opt_seq); } catch (std::exception const&) { return boost::none; } }
void msig2_t::operator()(Env const& env, JTx& jt) const { // VFALCO Inefficient pre-C++14 auto const sigs = sigs_; jt.signer = [sigs, &env](Env&, JTx& jt) { jt["SigningPubKey"] = ""; boost::optional<STObject> st; try { st = parse(jt.jv); } catch(parse_error const&) { env.test.log << pretty(jt.jv); throw; } auto& ja = jt["MultiSigners"]; ja.resize(sigs.size()); for (auto i = std::make_pair(0, sigs.begin()); i.first < sigs.size(); ++i.first, ++i.second) { auto const& sign_for = i.second->first; auto const& list = i.second->second; auto& ji = ja[i.first]["SigningFor"]; ji[jss::Account] = sign_for.human(); auto& js = ji["SigningAccounts"]; js.resize(list.size()); for (auto j = std::make_pair(0, list.begin()); j.first < list.size(); ++j.first, ++j.second) { auto& jj = js[j.first]["SigningAccount"]; jj[jss::Account] = j.second->human(); jj[jss::SigningPubKey] = strHex( j.second->pk().slice()); Serializer ss; ss.add32 (HashPrefix::txMultiSign); st->addWithoutSigningFields(ss); ss.add160(sign_for.id()); ss.add160(j.second->id()); auto const sig = ripple::sign( *publicKeyType(j.second->pk().slice()), j.second->sk(), ss.slice()); jj["MultiSignature"] = strHex(Slice{ sig.data(), sig.size() }); } } }; }
STValidation::STValidation( uint256 const& ledgerHash, std::uint32_t ledgerSeq, uint256 const& consensusHash, NetClock::time_point signTime, PublicKey const& publicKey, SecretKey const& secretKey, NodeID const& nodeID, bool isFull, FeeSettings const& fees, std::vector<uint256> const& amendments) : STObject(getFormat(), sfValidation), mNodeID(nodeID), mSeen(signTime) { // This is our own public key and it should always be valid. if (!publicKeyType(publicKey)) LogicError("Invalid validation public key"); assert(mNodeID.isNonZero()); setFieldH256(sfLedgerHash, ledgerHash); setFieldH256(sfConsensusHash, consensusHash); setFieldU32(sfSigningTime, signTime.time_since_epoch().count()); setFieldVL(sfSigningPubKey, publicKey.slice()); if (isFull) setFlag(kFullFlag); setFieldU32(sfLedgerSequence, ledgerSeq); if (fees.loadFee) setFieldU32(sfLoadFee, *fees.loadFee); if (fees.baseFee) setFieldU64(sfBaseFee, *fees.baseFee); if (fees.reserveBase) setFieldU32(sfReserveBase, *fees.reserveBase); if (fees.reserveIncrement) setFieldU32(sfReserveIncrement, *fees.reserveIncrement); if (!amendments.empty()) setFieldV256(sfAmendments, STVector256(sfAmendments, amendments)); setFlag(vfFullyCanonicalSig); auto const signingHash = getSigningHash(); setFieldVL( sfSignature, signDigest(getSignerPublic(), secretKey, signingHash)); setTrusted(); }
void sign (Json::Value& jv, Account const& account) { jv[jss::SigningPubKey] = strHex(account.pk().slice()); Serializer ss; ss.add32 (HashPrefix::txSign); parse(jv).add(ss); auto const sig = ripple::sign( *publicKeyType(account.pk().slice()), account.sk(), ss.slice()); jv[jss::TxnSignature] = strHex(Slice{ sig.data(), sig.size() }); }
NotTEC Transactor::checkSingleSign (PreclaimContext const& ctx) { auto const id = ctx.tx.getAccountID(sfAccount); auto const sle = ctx.view.read( keylet::account(id)); auto const hasAuthKey = sle->isFieldPresent (sfRegularKey); // Consistency: Check signature & verify the transaction's signing // public key is authorized for signing. auto const spk = ctx.tx.getSigningPubKey(); if (!publicKeyType (makeSlice (spk))) { JLOG(ctx.j.trace()) << "checkSingleSign: signing public key type is unknown"; return tefBAD_AUTH; // FIXME: should be better error! } auto const pkAccount = calcAccountID ( PublicKey (makeSlice (spk))); if (pkAccount == id) { // Authorized to continue. if (sle->isFlag(lsfDisableMaster)) return tefMASTER_DISABLED; } else if (hasAuthKey && (pkAccount == sle->getAccountID (sfRegularKey))) { // Authorized to continue. } else if (hasAuthKey) { JLOG(ctx.j.trace()) << "checkSingleSign: Not authorized to use account."; return tefBAD_AUTH; } else { JLOG(ctx.j.trace()) << "checkSingleSign: Not authorized to use account."; return tefBAD_AUTH_MASTER; } return tesSUCCESS; }
bool STValidation::isValid (uint256 const& signingHash) const { try { if (publicKeyType(getSignerPublic()) != KeyType::secp256k1) return false; return verifyDigest (getSignerPublic(), signingHash, makeSlice(getFieldVL (sfSignature)), getFlags () & vfFullyCanonicalSig); } catch (std::exception const&) { JLOG (debugLog().error()) << "Exception validating validation"; return false; } }
std::uint64_t SetRegularKey::calculateBaseFee ( PreclaimContext const& ctx) { auto const id = ctx.tx.getAccountID(sfAccount); auto const spk = ctx.tx.getSigningPubKey(); if (publicKeyType (makeSlice (spk))) { if (calcAccountID(PublicKey (makeSlice(spk))) == id) { auto const sle = ctx.view.read(keylet::account(id)); if (sle && (! (sle->getFlags () & lsfPasswordSpent))) { // flag is armed and they signed with the right account return 0; } } } return Transactor::calculateBaseFee (ctx); }
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 (); if (publicKeyType(line[sfPublicKey])) { 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; }
std::pair<bool, std::string> STTx::checkSingleSign () const { // We don't allow both a non-empty sfSigningPubKey and an sfSigners. // That would allow the transaction to be signed two ways. So if both // fields are present the signature is invalid. if (isFieldPresent (sfSigners)) return {false, "Cannot both single- and multi-sign."}; bool validSig = false; try { bool const fullyCanonical = (getFlags() & tfFullyCanonicalSig); auto const spk = getFieldVL (sfSigningPubKey); if (publicKeyType (makeSlice(spk))) { Blob const signature = getFieldVL (sfTxnSignature); Blob const data = getSigningData (*this); validSig = verify ( PublicKey (makeSlice(spk)), makeSlice(data), makeSlice(signature), fullyCanonical); } } catch (std::exception const&) { // Assume it was a signature failure. validSig = false; } if (validSig == false) return {false, "Invalid signature."}; return {true, ""}; }
Ed25519::Ed25519 ( SecretKey const& secretKey, PublicKey const& publicKey, Slice message) { if (publicKeyType (publicKey) != KeyType::ed25519) LogicError ("An Ed25519 public key is required."); // When PublicKey wraps an Ed25519 key it prefixes // the key itself with a 0xED byte. We carefully // skip that byte. std::memcpy ( payload_.data(), publicKey.data() + 1, publicKey.size() - 1); // Now sign: ed25519_sign ( message.data(), message.size(), secretKey.data(), payload_.data(), payload_.data() + pubkey_size_); }
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, ""}; }
NotTEC Transactor::checkMultiSign (PreclaimContext const& ctx) { auto const id = ctx.tx.getAccountID(sfAccount); // Get mTxnAccountID's SignerList and Quorum. std::shared_ptr<STLedgerEntry const> sleAccountSigners = ctx.view.read (keylet::signers(id)); // If the signer list doesn't exist the account is not multi-signing. if (!sleAccountSigners) { JLOG(ctx.j.trace()) << "applyTransaction: Invalid: Not a multi-signing account."; return tefNOT_MULTI_SIGNING; } // We have plans to support multiple SignerLists in the future. The // presence and defaulted value of the SignerListID field will enable that. assert (sleAccountSigners->isFieldPresent (sfSignerListID)); assert (sleAccountSigners->getFieldU32 (sfSignerListID) == 0); auto accountSigners = SignerEntries::deserialize (*sleAccountSigners, ctx.j, "ledger"); if (accountSigners.second != tesSUCCESS) return accountSigners.second; // Get the array of transaction signers. STArray const& txSigners (ctx.tx.getFieldArray (sfSigners)); // Walk the accountSigners performing a variety of checks and see if // the quorum is met. // Both the multiSigners and accountSigners are sorted by account. So // matching multi-signers to account signers should be a simple // linear walk. *All* signers must be valid or the transaction fails. std::uint32_t weightSum = 0; auto iter = accountSigners.first.begin (); for (auto const& txSigner : txSigners) { AccountID const txSignerAcctID = txSigner.getAccountID (sfAccount); // Attempt to match the SignerEntry with a Signer; while (iter->account < txSignerAcctID) { if (++iter == accountSigners.first.end ()) { JLOG(ctx.j.trace()) << "applyTransaction: Invalid SigningAccount.Account."; return tefBAD_SIGNATURE; } } if (iter->account != txSignerAcctID) { // The SigningAccount is not in the SignerEntries. JLOG(ctx.j.trace()) << "applyTransaction: Invalid SigningAccount.Account."; return tefBAD_SIGNATURE; } // We found the SigningAccount in the list of valid signers. Now we // need to compute the accountID that is associated with the signer's // public key. auto const spk = txSigner.getFieldVL (sfSigningPubKey); if (!publicKeyType (makeSlice(spk))) { JLOG(ctx.j.trace()) << "checkMultiSign: signing public key type is unknown"; return tefBAD_SIGNATURE; } AccountID const signingAcctIDFromPubKey = calcAccountID(PublicKey (makeSlice(spk))); // Verify that the signingAcctID and the signingAcctIDFromPubKey // belong together. Here is are the rules: // // 1. "Phantom account": an account that is not in the ledger // A. If signingAcctID == signingAcctIDFromPubKey and the // signingAcctID is not in the ledger then we have a phantom // account. // B. Phantom accounts are always allowed as multi-signers. // // 2. "Master Key" // A. signingAcctID == signingAcctIDFromPubKey, and signingAcctID // is in the ledger. // B. If the signingAcctID in the ledger does not have the // asfDisableMaster flag set, then the signature is allowed. // // 3. "Regular Key" // A. signingAcctID != signingAcctIDFromPubKey, and signingAcctID // is in the ledger. // B. If signingAcctIDFromPubKey == signingAcctID.RegularKey (from // ledger) then the signature is allowed. // // No other signatures are allowed. (January 2015) // In any of these cases we need to know whether the account is in // the ledger. Determine that now. auto sleTxSignerRoot = ctx.view.read (keylet::account(txSignerAcctID)); if (signingAcctIDFromPubKey == txSignerAcctID) { // Either Phantom or Master. Phantoms automatically pass. if (sleTxSignerRoot) { // Master Key. Account may not have asfDisableMaster set. std::uint32_t const signerAccountFlags = sleTxSignerRoot->getFieldU32 (sfFlags); if (signerAccountFlags & lsfDisableMaster) { JLOG(ctx.j.trace()) << "applyTransaction: Signer:Account lsfDisableMaster."; return tefMASTER_DISABLED; } } } else { // May be a Regular Key. Let's find out. // Public key must hash to the account's regular key. if (!sleTxSignerRoot) { JLOG(ctx.j.trace()) << "applyTransaction: Non-phantom signer lacks account root."; return tefBAD_SIGNATURE; } if (!sleTxSignerRoot->isFieldPresent (sfRegularKey)) { JLOG(ctx.j.trace()) << "applyTransaction: Account lacks RegularKey."; return tefBAD_SIGNATURE; } if (signingAcctIDFromPubKey != sleTxSignerRoot->getAccountID (sfRegularKey)) { JLOG(ctx.j.trace()) << "applyTransaction: Account doesn't match RegularKey."; return tefBAD_SIGNATURE; } } // The signer is legitimate. Add their weight toward the quorum. weightSum += iter->weight; } // Cannot perform transaction if quorum is not met. if (weightSum < sleAccountSigners->getFieldU32 (sfSignerQuorum)) { JLOG(ctx.j.trace()) << "applyTransaction: Signers failed to meet quorum."; return tefBAD_QUORUM; } // Met the quorum. Continue. return tesSUCCESS; }