|
35 | 35 | #include "ton/ton-tl.hpp" |
36 | 36 | #include "td/utils/JsonBuilder.h" |
37 | 37 | #include "auto/tl/ton_api_json.h" |
| 38 | +#include "keys/encryptor.h" |
| 39 | +#include "td/utils/port/path.h" |
38 | 40 | #include "tl/tl_json.h" |
39 | 41 |
|
40 | 42 | #include <cctype> |
@@ -283,6 +285,66 @@ td::Status SignFileQuery::receive(td::BufferSlice data) { |
283 | 285 | return td::Status::OK(); |
284 | 286 | } |
285 | 287 |
|
| 288 | +td::Status ExportAllPrivateKeysQuery::run() { |
| 289 | + TRY_RESULT_ASSIGN(directory_, tokenizer_.get_token<std::string>()); |
| 290 | + TRY_STATUS(tokenizer_.check_endl()); |
| 291 | + client_pk_ = ton::privkeys::Ed25519::random(); |
| 292 | + return td::Status::OK(); |
| 293 | +} |
| 294 | + |
| 295 | +td::Status ExportAllPrivateKeysQuery::send() { |
| 296 | + auto b = ton::create_serialize_tl_object<ton::ton_api::engine_validator_exportAllPrivateKeys>( |
| 297 | + client_pk_.compute_public_key().tl()); |
| 298 | + td::actor::send_closure(console_, &ValidatorEngineConsole::envelope_send_query, std::move(b), create_promise()); |
| 299 | + return td::Status::OK(); |
| 300 | +} |
| 301 | + |
| 302 | +td::Status ExportAllPrivateKeysQuery::receive(td::BufferSlice data) { |
| 303 | + TRY_RESULT_PREFIX(f, ton::fetch_tl_object<ton::ton_api::engine_validator_exportedPrivateKeys>(data.as_slice(), true), |
| 304 | + "received incorrect answer: "); |
| 305 | + // Private keys are encrypted using client-provided public key to avoid storing them in |
| 306 | + // non-secure buffers (not td::SecureString) |
| 307 | + TRY_RESULT_PREFIX(decryptor, client_pk_.create_decryptor(), "cannot create decryptor: "); |
| 308 | + TRY_RESULT_PREFIX(keys_data, decryptor->decrypt(f->encrypted_data_.as_slice()), "cannot decrypt data: "); |
| 309 | + SCOPE_EXIT { |
| 310 | + keys_data.as_slice().fill_zero_secure(); |
| 311 | + }; |
| 312 | + td::Slice slice = keys_data.as_slice(); |
| 313 | + if (slice.size() < 32) { |
| 314 | + return td::Status::Error("data is too small"); |
| 315 | + } |
| 316 | + slice.remove_suffix(32); |
| 317 | + std::vector<ton::PrivateKey> private_keys; |
| 318 | + while (!slice.empty()) { |
| 319 | + if (slice.size() < 4) { |
| 320 | + return td::Status::Error("unexpected end of data"); |
| 321 | + } |
| 322 | + td::uint32 size; |
| 323 | + td::MutableSlice{reinterpret_cast<char *>(&size), 4}.copy_from(slice.substr(0, 4)); |
| 324 | + if (size > slice.size()) { |
| 325 | + return td::Status::Error("unexpected end of data"); |
| 326 | + } |
| 327 | + slice.remove_prefix(4); |
| 328 | + TRY_RESULT_PREFIX(private_key, ton::PrivateKey::import(slice.substr(0, size)), "cannot parse private key: "); |
| 329 | + if (!private_key.exportable()) { |
| 330 | + return td::Status::Error("private key is not exportable"); |
| 331 | + } |
| 332 | + private_keys.push_back(std::move(private_key)); |
| 333 | + slice.remove_prefix(size); |
| 334 | + } |
| 335 | + |
| 336 | + TRY_STATUS_PREFIX(td::mkpath(directory_ + "/"), "cannot create directory " + directory_ + ": "); |
| 337 | + td::TerminalIO::out() << "exported " << private_keys.size() << " private keys" << "\n"; |
| 338 | + for (const ton::PrivateKey &private_key : private_keys) { |
| 339 | + std::string hash_hex = private_key.compute_short_id().bits256_value().to_hex(); |
| 340 | + TRY_STATUS_PREFIX(td::write_file(directory_ + "/" + hash_hex, private_key.export_as_slice()), |
| 341 | + "failed to write file: "); |
| 342 | + td::TerminalIO::out() << "pubkey_hash " << hash_hex << "\n"; |
| 343 | + } |
| 344 | + td::TerminalIO::out() << "written all files to " << directory_ << "\n"; |
| 345 | + return td::Status::OK(); |
| 346 | +} |
| 347 | + |
286 | 348 | td::Status AddAdnlAddrQuery::run() { |
287 | 349 | TRY_RESULT_ASSIGN(key_hash_, tokenizer_.get_token<ton::PublicKeyHash>()); |
288 | 350 | TRY_RESULT_ASSIGN(category_, tokenizer_.get_token<td::uint32>()); |
|
0 commit comments