bool ManifestCache::load ( DatabaseCon& dbCon, std::string const& dbTable, std::string const& configManifest, std::vector<std::string> const& configRevocation) { load (dbCon, dbTable); if (! configManifest.empty()) { auto mo = Manifest::make_Manifest ( beast::detail::base64_decode(configManifest)); if (! mo) { JLOG (j_.error()) << "Malformed validator_token in config"; return false; } if (mo->revoked()) { JLOG (j_.warn()) << "Configured manifest revokes public key"; } if (applyManifest (std::move(*mo)) == ManifestDisposition::invalid) { JLOG (j_.error()) << "Manifest in config was rejected"; return false; } } if (! configRevocation.empty()) { std::string revocationStr; revocationStr.reserve ( std::accumulate (configRevocation.cbegin(), configRevocation.cend(), std::size_t(0), [] (std::size_t init, std::string const& s) { return init + s.size(); })); for (auto const& line : configRevocation) revocationStr += beast::rfc2616::trim(line); auto mo = Manifest::make_Manifest ( beast::detail::base64_decode(revocationStr)); if (! mo || ! mo->revoked() || applyManifest (std::move(*mo)) == ManifestDisposition::invalid) { JLOG (j_.error()) << "Invalid validator key revocation in config"; return false; } } return true; }
bool Manifest::verify () const { STObject st (sfGeneric); SerialIter sit (serialized.data (), serialized.size ()); st.set (sit); // Signing key and signature are not required for // master key revocations if (! revoked () && ! ripple::verify (st, HashPrefix::manifest, signingKey)) return false; return ripple::verify ( st, HashPrefix::manifest, masterKey, sfMasterSignature); }
PGPPublicKey revoke_with_cert(const PGPPublicKey & pub, PGPPublicKey & revoke){ if (pub.get_ASCII_Armor() != 1){ throw std::runtime_error("Error: A public key is required."); } if (revoke.get_ASCII_Armor() != 1){ throw std::runtime_error("Error: A revocation signature is required."); } // only expects 1 signature packet if (!revoke.get_packets().size()){ throw std::runtime_error("Error: No packets found in revocation key."); } if (revoke.get_packets().size() > 1){ std::cerr << "Warning: Multiple packets found. Only reading first packet." << std::endl; } if (revoke.get_packets()[0] -> get_tag() != 2){ throw std::runtime_error("Error: Packet is not a signature packet"); } std::string raw = revoke.get_packets()[0] -> raw(); Tag2::Ptr tag2 = std::make_shared<Tag2>(raw); if ((tag2 -> get_type() != 0x20) && (tag2 -> get_type() != 0x28)){ std::stringstream s; s << static_cast <unsigned int> (tag2->get_type()); throw std::runtime_error("Error: Invalid signature type found: " + s.str()); } // which packet to look for uint8_t which = (tag2 -> get_type() == 0x20)?6:14; std::string r_keyid = tag2 -> get_keyid(); std::string k_keyid = ""; if (!r_keyid.size()){ throw std::runtime_error("Error: No key id found."); } // find key with key id for(Packet::Ptr const & p: pub.get_packets()){ if (p -> get_tag() == which){ raw = p -> raw(); Tag6 tag6(raw); k_keyid = tag6.get_keyid(); break; } } if (r_keyid != k_keyid){ throw std::runtime_error("Error: Revocation Certificate does not revoke this Public Key."); } if (!verify_revoke(pub, revoke)){ throw std::runtime_error("Error: This revocation certificate was not signed by this key."); } // Create output key PGPPublicKey revoked(pub); std::vector <Packet::Ptr> old_packets = pub.get_packets_clone(); std::vector <Packet::Ptr> new_packets; unsigned int i = 0; // push all packets up to and including revoked packet into new packets do{ new_packets.push_back(old_packets[i]); } while ((i < old_packets.size()) && (old_packets[i++] -> get_tag() != which)); // append revocation signature to key new_packets.push_back(tag2); // append rest of packets while (i < old_packets.size()){ new_packets.push_back(old_packets[i++]); } revoked.set_packets(new_packets); // clear out data tag2.reset(); old_packets.clear(); new_packets.clear(); return revoked; }
PGPPublicKey revoke_uid(PGPPublicKey & pub, PGPSecretKey & pri, const std::string passphrase, const uint8_t code, const std::string & reason){ if (pub.get_ASCII_Armor() != 1){ throw std::runtime_error("Error: A public key is required for the first argument."); } if (pri.get_ASCII_Armor() != 2){ throw std::runtime_error("Error: A private key is required for the second argument."); } Tag5::Ptr signer = find_signing_key(pri, 5); if (!signer){ throw std::runtime_error("Error: Private signing key not found"); } // find subkey Tag7::Ptr key = nullptr; std::vector <Packet::Ptr> packets = pri.get_packets(); for(Packet::Ptr const & p : packets){ if (p -> get_tag() == 7){ std::string data = p -> raw(); key = std::make_shared<Tag7>(data); break; } } if (!key){ throw std::runtime_error("Error: No Secret Subkey packet found."); } ID::Ptr uid = find_user_id(pri); if (!uid){ throw std::runtime_error("Error: No User ID packet found."); } Tag2::Ptr sig = create_sig_packet(0x30, signer); // add revocation subpacket std::vector <Tag2Subpacket::Ptr> hashed_subpackets = sig -> get_hashed_subpackets_clone(); Tag2Sub29::Ptr revoke = std::make_shared<Tag2Sub29>(); revoke -> set_code(code); revoke -> set_reason(reason); hashed_subpackets.push_back(revoke); sig -> set_hashed_subpackets(hashed_subpackets); // set signature data std::string hashed_data = to_sign_30(key, uid, sig); sig -> set_left16(hashed_data.substr(0, 2)); sig -> set_mpi(pka_sign(hashed_data, signer, passphrase, sig -> get_hash())); // Create output key PGPPublicKey revoked(pub); std::vector <Packet::Ptr> old_packets = pub.get_packets_clone(); std::vector <Packet::Ptr> new_packets; unsigned int i = 0; // push all packets up to and including revoked packet into new packets do{ new_packets.push_back(old_packets[i]); } while ((i < old_packets.size()) && (old_packets[i++] -> get_tag() != 13)); // append revocation signature to key new_packets.push_back(sig); // append rest of packets while (i < old_packets.size()){ new_packets.push_back(old_packets[i++]); } revoked.set_packets(new_packets); signer.reset(); key.reset(); uid.reset(); sig.reset(); return revoked; }
ListDisposition ValidatorList::verify ( Json::Value& list, PublicKey& pubKey, std::string const& manifest, std::string const& blob, std::string const& signature) { auto m = Manifest::make_Manifest (beast::detail::base64_decode(manifest)); if (! m || ! publisherLists_.count (m->masterKey)) return ListDisposition::untrusted; pubKey = m->masterKey; auto const revoked = m->revoked(); auto const result = publisherManifests_.applyManifest ( std::move(*m)); if (revoked && result == ManifestDisposition::accepted) { removePublisherList (pubKey); publisherLists_.erase (pubKey); } if (revoked || result == ManifestDisposition::invalid) return ListDisposition::untrusted; auto const sig = strUnHex(signature); auto const data = beast::detail::base64_decode (blob); if (! sig.second || ! ripple::verify ( publisherManifests_.getSigningKey(pubKey), makeSlice(data), makeSlice(sig.first))) return ListDisposition::invalid; Json::Reader r; if (! r.parse (data, list)) return ListDisposition::invalid; if (list.isMember("sequence") && list["sequence"].isInt() && list.isMember("expiration") && list["expiration"].isInt() && list.isMember("validators") && list["validators"].isArray()) { auto const sequence = list["sequence"].asUInt(); auto const expiration = TimeKeeper::time_point{ TimeKeeper::duration{list["expiration"].asUInt()}}; if (sequence < publisherLists_[pubKey].sequence || expiration <= timeKeeper_.now()) return ListDisposition::stale; else if (sequence == publisherLists_[pubKey].sequence) return ListDisposition::same_sequence; } else { return ListDisposition::invalid; } return ListDisposition::accepted; }
/** * @pre producers must be sorted from lowest to highest * @pre if proxy is set then no producers can be voted for * @pre every listed producer or proxy must have been previously registered * @pre voter must authorize this action * @pre voter must have previously staked some EOS for voting */ void system_contract::voteproducer( const account_name voter, const account_name proxy, const std::vector<account_name>& producers ) { require_auth( voter ); //validate input if ( proxy ) { eosio_assert( producers.size() == 0, "cannot vote for producers and proxy at same time" ); require_recipient( proxy ); } else { eosio_assert( producers.size() <= 30, "attempt to vote for too many producers" ); for( size_t i = 1; i < producers.size(); ++i ) { eosio_assert( producers[i-1] < producers[i], "producer votes must be unique and sorted" ); } } voters_table voters_tbl( _self, _self ); auto voter_it = voters_tbl.find( voter ); eosio_assert( 0 <= voter_it->staked.amount, "negative stake" ); eosio_assert( voter_it != voters_tbl.end() && ( 0 < voter_it->staked.amount || ( voter_it->is_proxy && 0 < voter_it->proxied_votes ) ), "no stake to vote" ); if ( voter_it->is_proxy ) { eosio_assert( proxy == 0 , "account registered as a proxy is not allowed to use a proxy" ); } //find old producers, update old proxy if needed const std::vector<account_name>* old_producers = nullptr; if( voter_it->proxy ) { if ( voter_it->proxy == proxy ) { return; // nothing changed } auto old_proxy = voters_tbl.find( voter_it->proxy ); eosio_assert( old_proxy != voters_tbl.end(), "old proxy not found" ); //data corruption voters_tbl.modify( old_proxy, 0, [&](auto& a) { a.proxied_votes -= uint64_t(voter_it->staked.amount); } ); if ( old_proxy->is_proxy ) { //if proxy stopped being a proxy, the votes were already taken back from producers by on( const unregister_proxy& ) old_producers = &old_proxy->producers; } } else { old_producers = &voter_it->producers; } //find new producers, update new proxy if needed const std::vector<account_name>* new_producers = nullptr; if ( proxy ) { auto new_proxy = voters_tbl.find( proxy ); eosio_assert( new_proxy != voters_tbl.end() && new_proxy->is_proxy, "proxy not found" ); voters_tbl.modify( new_proxy, 0, [&](auto& a) { a.proxied_votes += uint64_t(voter_it->staked.amount); } ); new_producers = &new_proxy->producers; } else { new_producers = &producers; } producers_table producers_tbl( _self, _self ); uint128_t votes = uint64_t(voter_it->staked.amount); if ( voter_it->is_proxy ) { votes += voter_it->proxied_votes; } if ( old_producers ) { //old_producers == nullptr if proxy has stopped being a proxy and votes were taken back from the producers at that moment //revoke votes only from no longer elected std::vector<account_name> revoked( old_producers->size() ); auto end_it = std::set_difference( old_producers->begin(), old_producers->end(), new_producers->begin(), new_producers->end(), revoked.begin() ); for ( auto it = revoked.begin(); it != end_it; ++it ) { auto prod = producers_tbl.find( *it ); eosio_assert( prod != producers_tbl.end(), "never existed producer" ); //data corruption producers_tbl.modify( prod, 0, [&]( auto& pi ) { pi.total_votes -= votes; } ); } } //update newly elected std::vector<account_name> elected( new_producers->size() ); auto end_it = elected.begin(); if( old_producers ) { end_it = std::set_difference( new_producers->begin(), new_producers->end(), old_producers->begin(), old_producers->end(), elected.begin() ); } else { end_it = std::copy( new_producers->begin(), new_producers->end(), elected.begin() ); } for ( auto it = elected.begin(); it != end_it; ++it ) { auto prod = producers_tbl.find( *it ); eosio_assert( prod != producers_tbl.end(), "producer is not registered" ); if ( proxy == 0 ) { //direct voting, in case of proxy voting update total_votes even for inactive producers eosio_assert( prod->active(), "producer is not currently registered" ); } producers_tbl.modify( prod, 0, [&]( auto& pi ) { pi.total_votes += votes; } ); } // save new values to the account itself voters_tbl.modify( voter_it, 0, [&](voter_info& a) { a.proxy = proxy; a.last_update = now(); a.producers = producers; }); }