mirror of
https://github.com/ton-blockchain/ton
synced 2025-02-12 11:12:16 +00:00
Export all keys command in validator-engine-console (#1412)
* Export all keys command in validator-engine-console * Use OPENSSL_cleanse in Bits256::fill_zero_s
This commit is contained in:
parent
4aa6412f9c
commit
9ae88d87e3
11 changed files with 173 additions and 6 deletions
|
@ -554,11 +554,7 @@ class BitArray {
|
|||
set_same(0);
|
||||
}
|
||||
void set_zero_s() {
|
||||
volatile uint8* p = data();
|
||||
auto x = m;
|
||||
while (x--) {
|
||||
*p++ = 0;
|
||||
}
|
||||
as_slice().fill_zero_secure();
|
||||
}
|
||||
void set_ones() {
|
||||
set_same(1);
|
||||
|
|
|
@ -28,7 +28,7 @@ namespace ton {
|
|||
namespace keyring {
|
||||
|
||||
KeyringImpl::PrivateKeyDescr::PrivateKeyDescr(PrivateKey private_key, bool is_temp)
|
||||
: public_key(private_key.compute_public_key()), is_temp(is_temp) {
|
||||
: public_key(private_key.compute_public_key()), private_key(private_key), is_temp(is_temp) {
|
||||
auto D = private_key.create_decryptor_async();
|
||||
D.ensure();
|
||||
decryptor_sign = D.move_as_ok();
|
||||
|
@ -190,6 +190,16 @@ void KeyringImpl::decrypt_message(PublicKeyHash key_hash, td::BufferSlice data,
|
|||
}
|
||||
}
|
||||
|
||||
void KeyringImpl::export_all_private_keys(td::Promise<std::vector<PrivateKey>> promise) {
|
||||
std::vector<PrivateKey> keys;
|
||||
for (auto& [_, descr] : map_) {
|
||||
if (!descr->is_temp && descr->private_key.exportable()) {
|
||||
keys.push_back(descr->private_key);
|
||||
}
|
||||
}
|
||||
promise.set_value(std::move(keys));
|
||||
}
|
||||
|
||||
td::actor::ActorOwn<Keyring> Keyring::create(std::string db_root) {
|
||||
return td::actor::create_actor<KeyringImpl>("keyring", db_root);
|
||||
}
|
||||
|
|
|
@ -44,6 +44,8 @@ class Keyring : public td::actor::Actor {
|
|||
|
||||
virtual void decrypt_message(PublicKeyHash key_hash, td::BufferSlice data, td::Promise<td::BufferSlice> promise) = 0;
|
||||
|
||||
virtual void export_all_private_keys(td::Promise<std::vector<PrivateKey>> promise) = 0;
|
||||
|
||||
static td::actor::ActorOwn<Keyring> create(std::string db_root);
|
||||
};
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ class KeyringImpl : public Keyring {
|
|||
td::actor::ActorOwn<DecryptorAsync> decryptor_sign;
|
||||
td::actor::ActorOwn<DecryptorAsync> decryptor_decrypt;
|
||||
PublicKey public_key;
|
||||
PrivateKey private_key;
|
||||
bool is_temp;
|
||||
PrivateKeyDescr(PrivateKey private_key, bool is_temp);
|
||||
};
|
||||
|
@ -56,6 +57,8 @@ class KeyringImpl : public Keyring {
|
|||
|
||||
void decrypt_message(PublicKeyHash key_hash, td::BufferSlice data, td::Promise<td::BufferSlice> promise) override;
|
||||
|
||||
void export_all_private_keys(td::Promise<std::vector<PrivateKey>> promise) override;
|
||||
|
||||
KeyringImpl(std::string db_root) : db_root_(db_root) {
|
||||
}
|
||||
|
||||
|
|
|
@ -725,6 +725,8 @@ engine.validator.perfTimerStats stats:(vector engine.validator.PerfTimerStatsByN
|
|||
|
||||
engine.validator.shardOutQueueSize size:long = engine.validator.ShardOutQueueSize;
|
||||
|
||||
engine.validator.exportedPrivateKeys encrypted_data:bytes = engine.validator.ExportedPrivateKeys;
|
||||
|
||||
|
||||
---functions---
|
||||
|
||||
|
@ -755,6 +757,7 @@ engine.validator.delListeningPort ip:int port:int categories:(vector int) priori
|
|||
engine.validator.delProxy out_ip:int out_port:int categories:(vector int) priority_categories:(vector int) = engine.validator.Success;
|
||||
|
||||
engine.validator.sign key_hash:int256 data:bytes = engine.validator.Signature;
|
||||
engine.validator.exportAllPrivateKeys encryption_key:PublicKey = engine.validator.ExportedPrivateKeys;
|
||||
|
||||
engine.validator.getStats = engine.validator.Stats;
|
||||
engine.validator.getConfig = engine.validator.JsonConfig;
|
||||
|
|
Binary file not shown.
|
@ -35,6 +35,8 @@
|
|||
#include "ton/ton-tl.hpp"
|
||||
#include "td/utils/JsonBuilder.h"
|
||||
#include "auto/tl/ton_api_json.h"
|
||||
#include "keys/encryptor.h"
|
||||
#include "td/utils/port/path.h"
|
||||
#include "tl/tl_json.h"
|
||||
|
||||
#include <cctype>
|
||||
|
@ -283,6 +285,66 @@ td::Status SignFileQuery::receive(td::BufferSlice data) {
|
|||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status ExportAllPrivateKeysQuery::run() {
|
||||
TRY_RESULT_ASSIGN(directory_, tokenizer_.get_token<std::string>());
|
||||
TRY_STATUS(tokenizer_.check_endl());
|
||||
client_pk_ = ton::privkeys::Ed25519::random();
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status ExportAllPrivateKeysQuery::send() {
|
||||
auto b = ton::create_serialize_tl_object<ton::ton_api::engine_validator_exportAllPrivateKeys>(
|
||||
client_pk_.compute_public_key().tl());
|
||||
td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise());
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status ExportAllPrivateKeysQuery::receive(td::BufferSlice data) {
|
||||
TRY_RESULT_PREFIX(f, ton::fetch_tl_object<ton::ton_api::engine_validator_exportedPrivateKeys>(data.as_slice(), true),
|
||||
"received incorrect answer: ");
|
||||
// Private keys are encrypted using client-provided public key to avoid storing them in
|
||||
// non-secure buffers (not td::SecureString)
|
||||
TRY_RESULT_PREFIX(decryptor, client_pk_.create_decryptor(), "cannot create decryptor: ");
|
||||
TRY_RESULT_PREFIX(keys_data, decryptor->decrypt(f->encrypted_data_.as_slice()), "cannot decrypt data: ");
|
||||
SCOPE_EXIT {
|
||||
keys_data.as_slice().fill_zero_secure();
|
||||
};
|
||||
td::Slice slice = keys_data.as_slice();
|
||||
if (slice.size() < 32) {
|
||||
return td::Status::Error("data is too small");
|
||||
}
|
||||
slice.remove_suffix(32);
|
||||
std::vector<ton::PrivateKey> private_keys;
|
||||
while (!slice.empty()) {
|
||||
if (slice.size() < 4) {
|
||||
return td::Status::Error("unexpected end of data");
|
||||
}
|
||||
td::uint32 size;
|
||||
td::MutableSlice{reinterpret_cast<char *>(&size), 4}.copy_from(slice.substr(0, 4));
|
||||
if (size > slice.size()) {
|
||||
return td::Status::Error("unexpected end of data");
|
||||
}
|
||||
slice.remove_prefix(4);
|
||||
TRY_RESULT_PREFIX(private_key, ton::PrivateKey::import(slice.substr(0, size)), "cannot parse private key: ");
|
||||
if (!private_key.exportable()) {
|
||||
return td::Status::Error("private key is not exportable");
|
||||
}
|
||||
private_keys.push_back(std::move(private_key));
|
||||
slice.remove_prefix(size);
|
||||
}
|
||||
|
||||
TRY_STATUS_PREFIX(td::mkpath(directory_ + "/"), "cannot create directory " + directory_ + ": ");
|
||||
td::TerminalIO::out() << "exported " << private_keys.size() << " private keys" << "\n";
|
||||
for (const ton::PrivateKey &private_key : private_keys) {
|
||||
std::string hash_hex = private_key.compute_short_id().bits256_value().to_hex();
|
||||
TRY_STATUS_PREFIX(td::write_file(directory_ + "/" + hash_hex, private_key.export_as_slice()),
|
||||
"failed to write file: ");
|
||||
td::TerminalIO::out() << "pubkey_hash " << hash_hex << "\n";
|
||||
}
|
||||
td::TerminalIO::out() << "written all files to " << directory_ << "\n";
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status AddAdnlAddrQuery::run() {
|
||||
TRY_RESULT_ASSIGN(key_hash_, tokenizer_.get_token<ton::PublicKeyHash>());
|
||||
TRY_RESULT_ASSIGN(category_, tokenizer_.get_token<td::uint32>());
|
||||
|
|
|
@ -413,6 +413,30 @@ class SignFileQuery : public Query {
|
|||
std::string out_file_;
|
||||
};
|
||||
|
||||
class ExportAllPrivateKeysQuery : public Query {
|
||||
public:
|
||||
ExportAllPrivateKeysQuery(td::actor::ActorId<ValidatorEngineConsole> console, Tokenizer tokenizer)
|
||||
: Query(console, std::move(tokenizer)) {
|
||||
}
|
||||
td::Status run() override;
|
||||
td::Status send() override;
|
||||
td::Status receive(td::BufferSlice R) override;
|
||||
static std::string get_name() {
|
||||
return "exportallprivatekeys";
|
||||
}
|
||||
static std::string get_help() {
|
||||
return "exportallprivatekeys <directory>\texports all private keys from validator engine and stores them to "
|
||||
"<directory>";
|
||||
}
|
||||
std::string name() const override {
|
||||
return get_name();
|
||||
}
|
||||
|
||||
private:
|
||||
std::string directory_;
|
||||
ton::PrivateKey client_pk_;
|
||||
};
|
||||
|
||||
class AddAdnlAddrQuery : public Query {
|
||||
public:
|
||||
AddAdnlAddrQuery(td::actor::ActorId<ValidatorEngineConsole> console, Tokenizer tokenizer)
|
||||
|
|
|
@ -112,6 +112,7 @@ void ValidatorEngineConsole::run() {
|
|||
add_query_runner(std::make_unique<QueryRunnerImpl<ExportPublicKeyFileQuery>>());
|
||||
add_query_runner(std::make_unique<QueryRunnerImpl<SignQuery>>());
|
||||
add_query_runner(std::make_unique<QueryRunnerImpl<SignFileQuery>>());
|
||||
add_query_runner(std::make_unique<QueryRunnerImpl<ExportAllPrivateKeysQuery>>());
|
||||
add_query_runner(std::make_unique<QueryRunnerImpl<AddAdnlAddrQuery>>());
|
||||
add_query_runner(std::make_unique<QueryRunnerImpl<AddDhtIdQuery>>());
|
||||
add_query_runner(std::make_unique<QueryRunnerImpl<AddValidatorPermanentKeyQuery>>());
|
||||
|
|
|
@ -3333,6 +3333,70 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_sign &que
|
|||
std::move(query.data_), std::move(P));
|
||||
}
|
||||
|
||||
void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_exportAllPrivateKeys &query,
|
||||
td::BufferSlice data, ton::PublicKeyHash src, td::uint32 perm,
|
||||
td::Promise<td::BufferSlice> promise) {
|
||||
if (!(perm & ValidatorEnginePermissions::vep_unsafe)) {
|
||||
promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::error, "not authorized")));
|
||||
return;
|
||||
}
|
||||
if (keyring_.empty()) {
|
||||
promise.set_value(create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "not started keyring")));
|
||||
return;
|
||||
}
|
||||
|
||||
ton::PublicKey client_pubkey = ton::PublicKey{query.encryption_key_};
|
||||
if (!client_pubkey.is_ed25519()) {
|
||||
promise.set_value(
|
||||
create_control_query_error(td::Status::Error(ton::ErrorCode::protoviolation, "encryption key is not Ed25519")));
|
||||
return;
|
||||
}
|
||||
|
||||
td::actor::send_closure(
|
||||
keyring_, &ton::keyring::Keyring::export_all_private_keys,
|
||||
[promise = std::move(promise),
|
||||
client_pubkey = std::move(client_pubkey)](td::Result<std::vector<ton::PrivateKey>> R) mutable {
|
||||
if (R.is_error()) {
|
||||
promise.set_value(create_control_query_error(R.move_as_error()));
|
||||
return;
|
||||
}
|
||||
// Private keys are encrypted using client-provided public key to avoid storing them in
|
||||
// non-secure buffers (not td::SecureString)
|
||||
std::vector<td::SecureString> serialized_keys;
|
||||
size_t data_size = 32;
|
||||
for (const ton::PrivateKey &key : R.ok()) {
|
||||
serialized_keys.push_back(key.export_as_slice());
|
||||
data_size += serialized_keys.back().size() + 4;
|
||||
}
|
||||
td::SecureString data{data_size};
|
||||
td::MutableSlice slice = data.as_mutable_slice();
|
||||
for (const td::SecureString &s : serialized_keys) {
|
||||
td::uint32 size = td::narrow_cast_safe<td::uint32>(s.size()).move_as_ok();
|
||||
CHECK(slice.size() >= size + 4);
|
||||
slice.copy_from(td::Slice{reinterpret_cast<const td::uint8 *>(&size), 4});
|
||||
slice.remove_prefix(4);
|
||||
slice.copy_from(s.as_slice());
|
||||
slice.remove_prefix(s.size());
|
||||
}
|
||||
CHECK(slice.size() == 32);
|
||||
td::Random::secure_bytes(slice);
|
||||
|
||||
auto r_encryptor = client_pubkey.create_encryptor();
|
||||
if (r_encryptor.is_error()) {
|
||||
promise.set_value(create_control_query_error(r_encryptor.move_as_error_prefix("cannot create encryptor: ")));
|
||||
return;
|
||||
}
|
||||
auto encryptor = r_encryptor.move_as_ok();
|
||||
auto r_encrypted = encryptor->encrypt(data.as_slice());
|
||||
if (r_encryptor.is_error()) {
|
||||
promise.set_value(create_control_query_error(r_encrypted.move_as_error_prefix("cannot encrypt data: ")));
|
||||
return;
|
||||
}
|
||||
promise.set_value(ton::create_serialize_tl_object<ton::ton_api::engine_validator_exportedPrivateKeys>(
|
||||
r_encrypted.move_as_ok()));
|
||||
});
|
||||
}
|
||||
|
||||
void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_setVerbosity &query, td::BufferSlice data,
|
||||
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise) {
|
||||
if (!(perm & ValidatorEnginePermissions::vep_default)) {
|
||||
|
|
|
@ -475,6 +475,8 @@ class ValidatorEngine : public td::actor::Actor {
|
|||
td::uint32 perm, td::Promise<td::BufferSlice> promise);
|
||||
void run_control_query(ton::ton_api::engine_validator_sign &query, td::BufferSlice data, ton::PublicKeyHash src,
|
||||
td::uint32 perm, td::Promise<td::BufferSlice> promise);
|
||||
void run_control_query(ton::ton_api::engine_validator_exportAllPrivateKeys &query, td::BufferSlice data,
|
||||
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise);
|
||||
void run_control_query(ton::ton_api::engine_validator_setVerbosity &query, td::BufferSlice data,
|
||||
ton::PublicKeyHash src, td::uint32 perm, td::Promise<td::BufferSlice> promise);
|
||||
void run_control_query(ton::ton_api::engine_validator_getStats &query, td::BufferSlice data, ton::PublicKeyHash src,
|
||||
|
|
Loading…
Reference in a new issue