set/get passport data

GitOrigin-RevId: 4fe8b44738ac91524a0d286db5d33d338b5b75e8
This commit is contained in:
Arseny Smirnov 2018-04-03 20:49:07 +03:00
parent 022bd092ce
commit 5e79712797
14 changed files with 443 additions and 95 deletions

View File

@ -131,6 +131,10 @@ class Secret {
int64 get_hash() const; int64 get_hash() const;
Secret clone() const; Secret clone() const;
static constexpr size_t size() {
return sizeof(secret_.raw);
}
private: private:
Secret(UInt256 secret, int64 hash); Secret(UInt256 secret, int64 hash);
UInt256 secret_; UInt256 secret_;

View File

@ -9,8 +9,10 @@
#include "td/telegram/files/FileManager.h" #include "td/telegram/files/FileManager.h"
#include "td/telegram/td_api.h" #include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h" #include "td/telegram/telegram_api.h"
#include "td/telegram/telegram_api.hpp"
#include "td/utils/misc.h" #include "td/utils/misc.h"
#include "td/utils/overloaded.h"
namespace td { namespace td {
@ -161,10 +163,9 @@ SecureFile get_secure_file(FileManager *file_manager, tl_object_ptr<telegram_api
LOG(ERROR) << "Wrong dc_id = " << dc_id; LOG(ERROR) << "Wrong dc_id = " << dc_id;
break; break;
} }
result.file_id = result.file_id = file_manager->register_remote(
file_manager->register_remote(FullRemoteFileLocation(FileType::SecureRaw, secure_file->id_, FullRemoteFileLocation(FileType::Secure, secure_file->id_, secure_file->access_hash_, DcId::internal(dc_id)),
secure_file->access_hash_, DcId::internal(dc_id)), FileLocationSource::FromServer, {}, 0, 0, "");
FileLocationSource::FromServer, {}, 0, 0, "");
result.encrypted_secret = secure_file->secret_.as_slice().str(); result.encrypted_secret = secure_file->secret_.as_slice().str();
result.file_hash = secure_file->file_hash_.as_slice().str(); result.file_hash = secure_file->file_hash_.as_slice().str();
break; break;
@ -189,13 +190,30 @@ vector<SecureFile> get_secure_files(FileManager *file_manager,
} }
telegram_api::object_ptr<telegram_api::InputSecureFile> get_input_secure_file_object(FileManager *file_manager, telegram_api::object_ptr<telegram_api::InputSecureFile> get_input_secure_file_object(FileManager *file_manager,
const SecureFile &file) { const SecureFile &file,
//TODO: SecureInputFile &input_file) {
return nullptr; CHECK(file_manager->get_file_view(file.file_id).file_id() ==
file_manager->get_file_view(input_file.file_id).file_id());
auto res = std::move(input_file.input_file);
if (res == nullptr) {
return file_manager->get_file_view(file.file_id).remote_location().as_input_secure_file();
}
telegram_api::downcast_call(*res, overloaded(
[&](telegram_api::inputSecureFileUploaded &uploaded) {
uploaded.secret_ = BufferSlice(file.encrypted_secret);
uploaded.file_hash_ = BufferSlice(file.file_hash);
},
[&](telegram_api::inputSecureFile &) { UNREACHABLE(); }));
return res;
} }
td_api::object_ptr<td_api::file> get_encrypted_file_object(FileManager *file_manager, const SecureFile &file) { td_api::object_ptr<td_api::file> get_encrypted_file_object(FileManager *file_manager, const SecureFile &file) {
return file_manager->get_file_object(file.file_id); auto file_view = file_manager->get_file_view(file.file_id);
auto file_id = file_manager->register_remote(
FullRemoteFileLocation(FileType::SecureRaw, file_view.remote_location().get_id(),
file_view.remote_location().get_access_hash(), file_view.remote_location().get_dc_id()),
FileLocationSource::FromServer, {}, 0, 0, "");
return file_manager->get_file_object(file_id);
} }
vector<td_api::object_ptr<td_api::file>> get_encrypted_files_object(FileManager *file_manager, vector<td_api::object_ptr<td_api::file>> get_encrypted_files_object(FileManager *file_manager,
@ -205,9 +223,15 @@ vector<td_api::object_ptr<td_api::file>> get_encrypted_files_object(FileManager
} }
vector<telegram_api::object_ptr<telegram_api::InputSecureFile>> get_input_secure_files_object( vector<telegram_api::object_ptr<telegram_api::InputSecureFile>> get_input_secure_files_object(
FileManager *file_manager, const vector<SecureFile> &file) { FileManager *file_manager, const vector<SecureFile> &files, vector<SecureInputFile> &input_files) {
//TODO: CHECK(files.size() == input_files.size());
return {}; vector<telegram_api::object_ptr<telegram_api::InputSecureFile>> res;
res.resize(files.size());
for (size_t i = 0; i < files.size(); i++) {
res[i] = get_input_secure_file_object(file_manager, files[i], input_files[i]);
}
LOG(ERROR) << res.size();
return res;
} }
bool operator==(const SecureData &lhs, const SecureData &rhs) { bool operator==(const SecureData &lhs, const SecureData &rhs) {
@ -281,11 +305,12 @@ td_api::object_ptr<td_api::encryptedPassportData> get_encrypted_passport_data_ob
bool is_plain = value.data.hash.empty(); bool is_plain = value.data.hash.empty();
return td_api::make_object<td_api::encryptedPassportData>( return td_api::make_object<td_api::encryptedPassportData>(
get_passport_data_type_object(value.type), is_plain ? string() : value.data.data, get_passport_data_type_object(value.type), is_plain ? string() : value.data.data,
get_encrypted_files_object(file_manager, value.files), is_plain ? value.data.data : string(), get_encrypted_file_object(file_manager, value.selfie)); get_encrypted_files_object(file_manager, value.files), is_plain ? value.data.data : string(),
get_encrypted_file_object(file_manager, value.selfie));
} }
telegram_api::object_ptr<telegram_api::inputSecureValue> get_input_secure_value_object( telegram_api::object_ptr<telegram_api::inputSecureValue> get_input_secure_value_object(
FileManager *file_manager, const EncryptedSecureValue &value) { FileManager *file_manager, const EncryptedSecureValue &value, std::vector<SecureInputFile> &input_files) {
bool is_plain = value.type == SecureValueType::PhoneNumber || value.type == SecureValueType::EmailAddress; bool is_plain = value.type == SecureValueType::PhoneNumber || value.type == SecureValueType::EmailAddress;
bool has_selfie = value.selfie.file_id.is_valid(); bool has_selfie = value.selfie.file_id.is_valid();
int32 flags = 0; int32 flags = 0;
@ -307,9 +332,9 @@ telegram_api::object_ptr<telegram_api::inputSecureValue> get_input_secure_value_
flags |= telegram_api::inputSecureValue::SELFIE_MASK; flags |= telegram_api::inputSecureValue::SELFIE_MASK;
} }
return telegram_api::make_object<telegram_api::inputSecureValue>( return telegram_api::make_object<telegram_api::inputSecureValue>(
0, get_secure_value_type_telegram_object(value.type), is_plain ? nullptr : get_secure_data_object(value.data), flags, get_secure_value_type_telegram_object(value.type), is_plain ? nullptr : get_secure_data_object(value.data),
get_input_secure_files_object(file_manager, value.files), std::move(plain_data), get_input_secure_files_object(file_manager, value.files, input_files), std::move(plain_data),
has_selfie ? get_input_secure_file_object(file_manager, value.selfie) : nullptr); has_selfie ? /*TODO*/ nullptr : nullptr);
} }
vector<td_api::object_ptr<td_api::encryptedPassportData>> get_encrypted_passport_data_object( vector<td_api::object_ptr<td_api::encryptedPassportData>> get_encrypted_passport_data_object(
@ -342,7 +367,8 @@ td_api::object_ptr<td_api::encryptedCredentials> get_encrypted_credentials_objec
credentials.encrypted_secret); credentials.encrypted_secret);
} }
Result<SecureValue> get_secure_value(td_api::object_ptr<td_api::inputPassportData> &&input_passport_data) { Result<SecureValue> get_secure_value(FileManager *file_manager,
td_api::object_ptr<td_api::inputPassportData> &&input_passport_data) {
if (input_passport_data == nullptr) { if (input_passport_data == nullptr) {
return Status::Error(400, "InputPassportData must not be empty"); return Status::Error(400, "InputPassportData must not be empty");
} }
@ -350,14 +376,31 @@ Result<SecureValue> get_secure_value(td_api::object_ptr<td_api::inputPassportDat
SecureValue res; SecureValue res;
res.type = get_secure_value_type_td_api(std::move(input_passport_data->type_)); res.type = get_secure_value_type_td_api(std::move(input_passport_data->type_));
res.data = std::move(input_passport_data->data_); res.data = std::move(input_passport_data->data_);
// res.files = TODO for (auto &file : input_passport_data->files_) {
TRY_RESULT(file_id, file_manager->get_input_file_id(FileType::Secure, std::move(file), DialogId{}, false, false,
false, true));
res.files.push_back(file_id);
}
// res.selfie = TODO // res.selfie = TODO
return res; return res;
} }
td_api::object_ptr<td_api::passportData> get_passport_data_object(FileManager *file_manager, const SecureValue &value) {
std::vector<td_api::object_ptr<td_api::file>> files;
files = transform(value.files, [&](FileId id) { return file_manager->get_file_object(id, true); });
//TODO selfie
return td_api::make_object<td_api::passportData>(get_passport_data_type_object(value.type), value.data,
std::move(files), nullptr);
}
Result<FileId> decrypt_secure_file(FileManager *file_manager, const secure_storage::Secret &secret, Result<FileId> decrypt_secure_file(FileManager *file_manager, const secure_storage::Secret &master_secret,
const SecureFile &secure_file) { const SecureFile &secure_file) {
return Status::Error("TODO"); TRY_RESULT(hash, secure_storage::ValueHash::create(secure_file.file_hash));
TRY_RESULT(encrypted_secret, secure_storage::EncryptedSecret::create(secure_file.encrypted_secret));
TRY_RESULT(secret, encrypted_secret.decrypt(PSLICE() << master_secret.as_slice() << hash.as_slice()));
FileEncryptionKey key{secret};
key.set_value_hash(hash);
file_manager->set_encryption_key(secure_file.file_id, std::move(key));
return secure_file.file_id;
} }
Result<vector<FileId>> decrypt_secure_files(FileManager *file_manager, const secure_storage::Secret &secret, Result<vector<FileId>> decrypt_secure_files(FileManager *file_manager, const secure_storage::Secret &secret,
@ -391,9 +434,9 @@ Result<SecureValue> decrypt_encrypted_secure_value(FileManager *file_manager, co
default: { default: {
TRY_RESULT(data, decrypt_secure_data(secret, encrypted_secure_value.data)); TRY_RESULT(data, decrypt_secure_data(secret, encrypted_secure_value.data));
res.data = std::move(data); res.data = std::move(data);
TRY_RESULT(files, decrypt_secure_files(file_manager, secret, encrypted_secure_value.files));
res.files = std::move(files);
// TODO // TODO
//TRY_RESULT(files, decrypt_secure_files(file_manager, secret, encrypted_secure_value.files));
//res.files = std::move(files);
//TRY_RESULT(selfie, decrypt_secure_file(file_manager, secret, encrypted_secure_value.selfie)); //TRY_RESULT(selfie, decrypt_secure_file(file_manager, secret, encrypted_secure_value.selfie));
//res.selfie = std::move(selfie); //res.selfie = std::move(selfie);
break; break;
@ -404,8 +447,28 @@ Result<SecureValue> decrypt_encrypted_secure_value(FileManager *file_manager, co
SecureFile encrypt_secure_file(FileManager *file_manager, const secure_storage::Secret &master_secret, FileId file, SecureFile encrypt_secure_file(FileManager *file_manager, const secure_storage::Secret &master_secret, FileId file,
string &to_hash) { string &to_hash) {
//TODO: auto file_view = file_manager->get_file_view(file);
return SecureFile{}; if (file_view.empty()) {
return {};
}
if (!file_view.encryption_key().is_secure()) {
LOG(ERROR) << "File has no encryption key";
return {};
}
if (!file_view.encryption_key().has_value_hash()) {
LOG(ERROR) << "File has no hash";
return {};
}
auto value_hash = file_view.encryption_key().value_hash();
auto secret = file_view.encryption_key().secret();
SecureFile res;
res.file_id = file;
res.file_hash = value_hash.as_slice().str();
res.encrypted_secret = secret.encrypt(PSLICE() << master_secret.as_slice() << value_hash.as_slice()).as_slice().str();
to_hash.append(res.file_hash);
to_hash.append(secret.as_slice().str());
return res;
} }
vector<SecureFile> encrypt_secure_files(FileManager *file_manager, const secure_storage::Secret &master_secret, vector<SecureFile> encrypt_secure_files(FileManager *file_manager, const secure_storage::Secret &master_secret,
@ -442,8 +505,8 @@ EncryptedSecureValue encrypt_secure_value(FileManager *file_manager, const secur
default: { default: {
string to_hash; string to_hash;
res.data = encrypt_secure_data(master_secret, secure_value.data, to_hash); res.data = encrypt_secure_data(master_secret, secure_value.data, to_hash);
res.files = encrypt_secure_files(file_manager, master_secret, secure_value.files, to_hash);
// TODO // TODO
//res.files = encrypt_secure_files(file_manager, master_secret, secure_value.files, to_hash);
//res.selfie = encrypt_secure_file(file_manager, master_secret, secure_value.selfie, to_hash); //res.selfie = encrypt_secure_file(file_manager, master_secret, secure_value.selfie, to_hash);
res.hash = ss::calc_value_hash(to_hash).as_slice().str(); res.hash = ss::calc_value_hash(to_hash).as_slice().str();
break; break;

View File

@ -59,8 +59,13 @@ SecureFile get_secure_file(FileManager *file_manager, tl_object_ptr<telegram_api
vector<SecureFile> get_secure_files(FileManager *file_manager, vector<SecureFile> get_secure_files(FileManager *file_manager,
vector<tl_object_ptr<telegram_api::SecureFile>> &&secure_files); vector<tl_object_ptr<telegram_api::SecureFile>> &&secure_files);
struct SecureInputFile {
FileId file_id;
tl_object_ptr<telegram_api::InputSecureFile> input_file;
};
telegram_api::object_ptr<telegram_api::InputSecureFile> get_input_secure_file_object(FileManager *file_manager, telegram_api::object_ptr<telegram_api::InputSecureFile> get_input_secure_file_object(FileManager *file_manager,
const SecureFile &file); const SecureFile &file,
SecureInputFile &input_file);
td_api::object_ptr<td_api::file> get_encrypted_file_object(FileManager *file_manager, const SecureFile &file); td_api::object_ptr<td_api::file> get_encrypted_file_object(FileManager *file_manager, const SecureFile &file);
@ -68,7 +73,7 @@ vector<td_api::object_ptr<td_api::file>> get_encrypted_files_object(FileManager
const vector<SecureFile> &files); const vector<SecureFile> &files);
vector<telegram_api::object_ptr<telegram_api::InputSecureFile>> get_input_secure_files_object( vector<telegram_api::object_ptr<telegram_api::InputSecureFile>> get_input_secure_files_object(
FileManager *file_manager, const vector<SecureFile> &file); FileManager *file_manager, const vector<SecureFile> &file, vector<SecureInputFile> &input_files);
struct SecureData { struct SecureData {
string data; string data;
@ -103,7 +108,7 @@ vector<EncryptedSecureValue> get_encrypted_secure_values(
td_api::object_ptr<td_api::encryptedPassportData> get_encrypted_passport_data_object(FileManager *file_manager, td_api::object_ptr<td_api::encryptedPassportData> get_encrypted_passport_data_object(FileManager *file_manager,
const EncryptedSecureValue &value); const EncryptedSecureValue &value);
telegram_api::object_ptr<telegram_api::inputSecureValue> get_input_secure_value_object( telegram_api::object_ptr<telegram_api::inputSecureValue> get_input_secure_value_object(
FileManager *file_manager, const EncryptedSecureValue &value); FileManager *file_manager, const EncryptedSecureValue &value, vector<SecureInputFile> &input_files);
vector<td_api::object_ptr<td_api::encryptedPassportData>> get_encrypted_passport_data_object( vector<td_api::object_ptr<td_api::encryptedPassportData>> get_encrypted_passport_data_object(
FileManager *file_manager, const vector<EncryptedSecureValue> &values); FileManager *file_manager, const vector<EncryptedSecureValue> &values);
@ -129,7 +134,10 @@ class SecureValue {
FileId selfie; FileId selfie;
}; };
Result<SecureValue> get_secure_value(td_api::object_ptr<td_api::inputPassportData> &&input_passport_data); Result<SecureValue> get_secure_value(FileManager *file_manager,
td_api::object_ptr<td_api::inputPassportData> &&input_passport_data);
td_api::object_ptr<td_api::passportData> get_passport_data_object(FileManager *file_manager, const SecureValue &value);
Result<FileId> decrypt_secure_file(FileManager *file_manager, const secure_storage::Secret &secret, Result<FileId> decrypt_secure_file(FileManager *file_manager, const secure_storage::Secret &secret,
const SecureFile &secure_file); const SecureFile &secure_file);

View File

@ -3800,16 +3800,17 @@ class GetTermsOfServiceRequest : public RequestActor<string> {
} }
}; };
using TdApiSecureValue = td_api::object_ptr<td_api::passportData>;
class GetSecureValue : public NetQueryCallback { class GetSecureValue : public NetQueryCallback {
public: public:
GetSecureValue(std::string password, SecureValueType type, Promise<SecureValue> promise) GetSecureValue(std::string password, SecureValueType type, Promise<TdApiSecureValue> promise)
: password_(std::move(password)), type_(type), promise_(std::move(promise)) { : password_(std::move(password)), type_(type), promise_(std::move(promise)) {
} }
private: private:
string password_; string password_;
SecureValueType type_; SecureValueType type_;
Promise<SecureValue> promise_; Promise<TdApiSecureValue> promise_;
optional<EncryptedSecureValue> encrypted_secure_value_; optional<EncryptedSecureValue> encrypted_secure_value_;
optional<secure_storage::Secret> secret_; optional<secure_storage::Secret> secret_;
@ -3832,8 +3833,12 @@ class GetSecureValue : public NetQueryCallback {
if (!encrypted_secure_value_ || !secret_) { if (!encrypted_secure_value_ || !secret_) {
return; return;
} }
promise_.set_result(decrypt_encrypted_secure_value(G()->td().get_actor_unsafe()->file_manager_.get(), *secret_, auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get();
*encrypted_secure_value_)); auto r_secure_value = decrypt_encrypted_secure_value(file_manager, *secret_, *encrypted_secure_value_);
if (r_secure_value.is_error()) {
return on_error(r_secure_value.move_as_error());
}
promise_.set_result(get_passport_data_object(file_manager, r_secure_value.move_as_ok()));
stop(); stop();
} }
void start_up() override { void start_up() override {
@ -3868,18 +3873,61 @@ class GetSecureValue : public NetQueryCallback {
class SetSecureValue : public NetQueryCallback { class SetSecureValue : public NetQueryCallback {
public: public:
SetSecureValue(string password, SecureValue secure_value, Promise<Unit> promise) SetSecureValue(string password, SecureValue secure_value, Promise<TdApiSecureValue> promise)
: password_(std::move(password)), secure_value_(std::move(secure_value)), promise_(std::move(promise)) { : password_(std::move(password)), secure_value_(std::move(secure_value)), promise_(std::move(promise)) {
} }
private: private:
string password_; string password_;
SecureValue secure_value_; SecureValue secure_value_;
Promise<Unit> promise_; Promise<TdApiSecureValue> promise_;
optional<secure_storage::Secret> secret_; optional<secure_storage::Secret> secret_;
size_t files_left_to_upload_ = 0;
vector<SecureInputFile> to_upload_;
class UploadCallback;
std::shared_ptr<UploadCallback> upload_callback_;
enum class State { WaitSecret, WaitSetValue } state_ = State::WaitSecret; enum class State { WaitSecret, WaitSetValue } state_ = State::WaitSecret;
class UploadCallback : public FileManager::UploadCallback {
public:
explicit UploadCallback(ActorId<SetSecureValue> actor_id) : actor_id_(actor_id) {
}
private:
ActorId<SetSecureValue> actor_id_;
void on_upload_ok(FileId file_id, tl_object_ptr<telegram_api::InputFile> input_file) override {
UNREACHABLE();
}
void on_upload_encrypted_ok(FileId file_id, tl_object_ptr<telegram_api::InputEncryptedFile> input_file) override {
UNREACHABLE();
}
void on_upload_secure_ok(FileId file_id, tl_object_ptr<telegram_api::InputSecureFile> input_file) override {
send_closure(actor_id_, &SetSecureValue::on_upload_ok, file_id, std::move(input_file));
}
void on_upload_error(FileId file_id, Status error) override {
send_closure(actor_id_, &SetSecureValue::on_upload_error, file_id, std::move(error));
}
};
void on_upload_ok(FileId file_id, tl_object_ptr<telegram_api::InputSecureFile> input_file) {
for (auto &info : to_upload_) {
if (info.file_id != file_id) {
continue;
}
CHECK(!info.input_file);
info.input_file = std::move(input_file);
CHECK(files_left_to_upload_ != 0);
files_left_to_upload_--;
return loop();
}
UNREACHABLE();
}
void on_upload_error(FileId file_id, Status error) {
return on_error(std::move(error));
}
void on_error(Status status) { void on_error(Status status) {
promise_.set_error(std::move(status)); promise_.set_error(std::move(status));
stop(); stop();
@ -3900,6 +3948,31 @@ class SetSecureValue : public NetQueryCallback {
PromiseCreator::lambda([actor_id = actor_id(this)](Result<secure_storage::Secret> r_secret) { PromiseCreator::lambda([actor_id = actor_id(this)](Result<secure_storage::Secret> r_secret) {
send_closure(actor_id, &SetSecureValue::on_secret, std::move(r_secret), true); send_closure(actor_id, &SetSecureValue::on_secret, std::move(r_secret), true);
})); }));
auto *file_manager = G()->file_manager().get_actor_unsafe();
// Remove duplicated files
for (auto it = secure_value_.files.begin(); it != secure_value_.files.end();) {
bool is_duplicate = false;
for (auto pit = secure_value_.files.begin(); pit != it; pit++) {
if (file_manager->get_file_view(*it).file_id() == file_manager->get_file_view(*pit).file_id()) {
is_duplicate = true;
break;
}
}
if (is_duplicate) {
it = secure_value_.files.erase(it);
} else {
it++;
}
}
to_upload_.resize(secure_value_.files.size());
upload_callback_ = std::make_shared<UploadCallback>(actor_id(this));
for (size_t i = 0; i < to_upload_.size(); i++) {
to_upload_[i].file_id = file_manager->dup_file_id(secure_value_.files[i]);
file_manager->upload(to_upload_[i].file_id, upload_callback_, 1, 0);
files_left_to_upload_++;
}
} }
void loop() override { void loop() override {
@ -3907,10 +3980,14 @@ class SetSecureValue : public NetQueryCallback {
if (!secret_) { if (!secret_) {
return; return;
} }
if (files_left_to_upload_ != 0) {
return;
}
auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get(); auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get();
auto save_secure_value = telegram_api::account_saveSecureValue( auto input_secure_value = get_input_secure_value_object(
get_input_secure_value_object(file_manager, encrypt_secure_value(file_manager, *secret_, secure_value_)), file_manager, encrypt_secure_value(file_manager, *secret_, secure_value_), to_upload_);
secret_.value().get_hash()); auto save_secure_value =
telegram_api::account_saveSecureValue(std::move(input_secure_value), secret_.value().get_hash());
LOG(ERROR) << to_string(save_secure_value); LOG(ERROR) << to_string(save_secure_value);
auto query = G()->net_query_creator().create(create_storer(save_secure_value)); auto query = G()->net_query_creator().create(create_storer(save_secure_value));
@ -3925,6 +4002,28 @@ class SetSecureValue : public NetQueryCallback {
} }
auto result = r_result.move_as_ok(); auto result = r_result.move_as_ok();
LOG(ERROR) << to_string(result); LOG(ERROR) << to_string(result);
auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get();
auto encrypted_secure_value = get_encrypted_secure_value(file_manager, std::move(result));
if (secure_value_.files.size() != encrypted_secure_value.files.size()) {
return on_error(Status::Error("Different files count"));
}
for (size_t i = 0; i < secure_value_.files.size(); i++) {
auto file_view = file_manager->get_file_view(secure_value_.files[i]);
CHECK(!file_view.empty());
CHECK(file_view.encryption_key().has_value_hash());
if (file_view.encryption_key().value_hash().as_slice() != encrypted_secure_value.files[i].file_hash) {
LOG(ERROR) << "hash mismatch";
continue;
}
auto status = file_manager->merge(encrypted_secure_value.files[i].file_id, secure_value_.files[i]);
LOG_IF(ERROR, status.is_error()) << status.error();
}
auto r_secure_value = decrypt_encrypted_secure_value(file_manager, *secret_, encrypted_secure_value);
if (r_secure_value.is_error()) {
return on_error(r_secure_value.move_as_error());
}
promise_.set_result(get_passport_data_object(file_manager, r_secure_value.move_as_ok()));
stop();
} }
}; };
@ -6205,7 +6304,9 @@ void Td::on_request(uint64 id, td_api::uploadFile &request) {
auto file_type = request.file_type_ == nullptr ? FileType::Temp : from_td_api(*request.file_type_); auto file_type = request.file_type_ == nullptr ? FileType::Temp : from_td_api(*request.file_type_);
bool is_secret = file_type == FileType::Encrypted || file_type == FileType::EncryptedThumbnail; bool is_secret = file_type == FileType::Encrypted || file_type == FileType::EncryptedThumbnail;
auto r_file_id = file_manager_->get_input_file_id(file_type, request.file_, DialogId(), false, is_secret, true); bool is_secure = file_type == FileType::Secure;
auto r_file_id = file_manager_->get_input_file_id(file_type, request.file_, DialogId(), false, is_secret,
!is_secure && !is_secret, is_secure);
if (r_file_id.is_error()) { if (r_file_id.is_error()) {
return send_error_raw(id, 400, r_file_id.error().message()); return send_error_raw(id, 400, r_file_id.error().message());
} }
@ -6951,15 +7052,12 @@ void Td::on_request(uint64 id, td_api::getPassportData &request) {
CHECK_AUTH(); CHECK_AUTH();
CHECK_IS_USER(); CHECK_IS_USER();
CLEAN_INPUT_STRING(request.password_); CLEAN_INPUT_STRING(request.password_);
CREATE_REQUEST_PROMISE(promise);
if (request.type_ == nullptr) { if (request.type_ == nullptr) {
return send_error_raw(id, 400, "Type must not be empty"); return promise.set_error(Status::Error(400, "Type must not be empty"));
} }
create_actor<GetSecureValue>("GetSecureValue", std::move(request.password_), create_actor<GetSecureValue>("GetSecureValue", std::move(request.password_),
get_secure_value_type_td_api(std::move(request.type_)), get_secure_value_type_td_api(std::move(request.type_)), std::move(promise))
PromiseCreator::lambda([](Result<SecureValue> r_value) {
LOG_IF(ERROR, r_value.is_error()) << r_value.error();
LOG_IF(ERROR, r_value.is_ok()) << r_value.ok().data;
}))
.release(); .release();
} }
@ -6967,13 +7065,13 @@ void Td::on_request(uint64 id, td_api::setPassportData &request) {
CHECK_AUTH(); CHECK_AUTH();
CHECK_IS_USER(); CHECK_IS_USER();
CLEAN_INPUT_STRING(request.password_); CLEAN_INPUT_STRING(request.password_);
auto r_secure_value = get_secure_value(std::move(request.value_)); CREATE_REQUEST_PROMISE(promise);
auto r_secure_value = get_secure_value(file_manager_.get(), std::move(request.value_));
if (r_secure_value.is_error()) { if (r_secure_value.is_error()) {
return send_closure(actor_id(this), &Td::send_error, id, r_secure_value.move_as_error()); return promise.set_error(r_secure_value.move_as_error());
} }
create_actor<SetSecureValue>( create_actor<SetSecureValue>("SetSecureValue", std::move(request.password_), r_secure_value.move_as_ok(),
"SetSecureValue", std::move(request.password_), r_secure_value.move_as_ok(), std::move(promise))
PromiseCreator::lambda([](Result<Unit> result) { LOG_IF(ERROR, result.is_error()) << result.error(); }))
.release(); .release();
} }

View File

@ -946,21 +946,24 @@ class CliClient final : public Actor {
} }
return make_tl_object<td_api::passportDataTypePassport>(); return make_tl_object<td_api::passportDataTypePassport>();
} }
static tl_object_ptr<td_api::inputPassportData> as_input_passport_data(string passport_data_type) { static tl_object_ptr<td_api::inputPassportData> as_input_passport_data(string passport_data_type, string file) {
return nullptr;
/* TODO
vector<td_api::object_ptr<td_api::InputFile>> files; vector<td_api::object_ptr<td_api::InputFile>> files;
LOG(ERROR) << "FILE " << file;
if (!file.empty()) {
files.push_back(make_tl_object<td_api::inputFileLocal>(file));
}
auto data_type = as_passport_data_type(passport_data_type);
string data;
if (passport_data_type == "address" || passport_data_type == "a") { if (passport_data_type == "address" || passport_data_type == "a") {
return make_tl_object<td_api::inputPassportDataAddress>("cucumber lives here", std::move(files)); data = "cucumber lives here";
} else if (passport_data_type == "email" || passport_data_type == "e") {
data = "{todo}";
} else if (passport_data_type == "phone" || passport_data_type == "p") {
data = "{todo}";
} else {
data = "I am cucumber";
} }
if (passport_data_type == "email" || passport_data_type == "e") { return make_tl_object<td_api::inputPassportData>(std::move(data_type), std::move(data), std::move(files), nullptr);
return make_tl_object<td_api::inputPassportDataEmailAddress>("{todo}");
}
if (passport_data_type == "phone" || passport_data_type == "p") {
return make_tl_object<td_api::inputPassportDataPhoneNumber>("{todo}");
}
return make_tl_object<td_api::inputPassportDataIdentity>("I am cucumber", std::move(files));
*/
} }
static td_api::object_ptr<td_api::Object> execute(tl_object_ptr<td_api::Function> f) { static td_api::object_ptr<td_api::Object> execute(tl_object_ptr<td_api::Function> f) {
@ -1086,8 +1089,10 @@ class CliClient final : public Actor {
} else if (op == "spd") { } else if (op == "spd") {
string password; string password;
string passport_data_type; string passport_data_type;
std::tie(password, passport_data_type) = split(args); string file;
send_request(make_tl_object<td_api::setPassportData>(as_input_passport_data(passport_data_type), password)); std::tie(password, args) = split(args);
std::tie(passport_data_type, file) = split(args);
send_request(make_tl_object<td_api::setPassportData>(as_input_passport_data(passport_data_type, file), password));
} else if (op == "pdu" || op == "processDcUpdate") { } else if (op == "pdu" || op == "processDcUpdate") {
string dc_id; string dc_id;
string ip_port; string ip_port;

View File

@ -36,7 +36,7 @@ FileDownloader::FileDownloader(const FullRemoteFileLocation &remote, const Local
, callback_(std::move(callback)) , callback_(std::move(callback))
, is_small_(is_small) , is_small_(is_small)
, search_file_(search_file) { , search_file_(search_file) {
if (!encryption_key.empty()) { if (!encryption_key.is_secret()) {
set_ordered_flag(true); set_ordered_flag(true);
} }
} }
@ -48,6 +48,12 @@ Result<FileLoader::FileInfo> FileDownloader::init() {
if (local_.type() == LocalFileLocation::Type::Full) { if (local_.type() == LocalFileLocation::Type::Full) {
return Status::Error("File is already downloaded"); return Status::Error("File is already downloaded");
} }
if (encryption_key_.is_secure() && !encryption_key_.has_value_hash()) {
LOG(ERROR) << "Can't download Secure file with unknown value_hash";
}
if (remote_.file_type_ == FileType::Secure) {
size_ = 0;
}
int ready_part_count = 0; int ready_part_count = 0;
int32 part_size = 0; int32 part_size = 0;
if (local_.type() == LocalFileLocation::Type::Partial) { if (local_.type() == LocalFileLocation::Type::Partial) {
@ -56,7 +62,7 @@ Result<FileLoader::FileInfo> FileDownloader::init() {
auto result_fd = FileFd::open(path_, FileFd::Write | FileFd::Read); auto result_fd = FileFd::open(path_, FileFd::Write | FileFd::Read);
// TODO: check timestamps.. // TODO: check timestamps..
if (result_fd.is_ok()) { if (result_fd.is_ok()) {
if (!encryption_key_.empty()) { if (encryption_key_.is_secret()) {
CHECK(partial.iv_.size() == 32) << partial.iv_.size(); CHECK(partial.iv_.size() == 32) << partial.iv_.size();
encryption_key_.mutable_iv() = as<UInt256>(partial.iv_.data()); encryption_key_.mutable_iv() = as<UInt256>(partial.iv_.data());
next_part_ = partial.ready_part_count_; next_part_ = partial.ready_part_count_;
@ -104,13 +110,22 @@ Status FileDownloader::on_ok(int64 size) {
auto dir = get_files_dir(remote_.file_type_); auto dir = get_files_dir(remote_.file_type_);
std::string path; std::string path;
fd_.close();
if (encryption_key_.is_secure()) {
TRY_RESULT(file_path, open_temp_file(remote_.file_type_));
string tmp_path;
std::tie(std::ignore, tmp_path) = std::move(file_path);
TRY_STATUS(secure_storage::decrypt_file(encryption_key_.secret(), encryption_key_.value_hash(), path_, tmp_path));
path_ = std::move(tmp_path);
TRY_RESULT(path_stat, stat(path_));
size = path_stat.size_;
}
if (only_check_) { if (only_check_) {
path = path_; path = path_;
} else { } else {
TRY_RESULT(perm_path, create_from_temp(path_, dir, name_)); TRY_RESULT(perm_path, create_from_temp(path_, dir, name_));
path = std::move(perm_path); path = std::move(perm_path);
} }
fd_.close();
callback_->on_ok(FullLocalFileLocation(remote_.file_type_, std::move(path), 0), size); callback_->on_ok(FullLocalFileLocation(remote_.file_type_, std::move(path), 0), size);
return Status::OK(); return Status::OK();
} }
@ -192,7 +207,7 @@ Result<bool> FileDownloader::should_restart_part(Part part, NetQueryPtr &net_que
return false; return false;
} }
Result<std::pair<NetQueryPtr, bool>> FileDownloader::start_part(Part part, int32 part_count) { Result<std::pair<NetQueryPtr, bool>> FileDownloader::start_part(Part part, int32 part_count) {
if (!encryption_key_.empty()) { if (encryption_key_.is_secret()) {
part.size = (part.size + 15) & ~15; // fix for last part part.size = (part.size + 15) & ~15; // fix for last part
} }
// auto size = part.size; // auto size = part.size;
@ -279,7 +294,7 @@ Result<size_t> FileDownloader::process_part(Part part, NetQueryPtr net_query) {
} }
auto padded_size = part.size; auto padded_size = part.size;
if (!encryption_key_.empty()) { if (encryption_key_.is_secret()) {
padded_size = (part.size + 15) & ~15; padded_size = (part.size + 15) & ~15;
} }
LOG(INFO) << "Got " << bytes.size() << " padded_size=" << padded_size; LOG(INFO) << "Got " << bytes.size() << " padded_size=" << padded_size;
@ -304,7 +319,7 @@ Result<size_t> FileDownloader::process_part(Part part, NetQueryPtr net_query) {
ctr_state.init(key, iv); ctr_state.init(key, iv);
ctr_state.decrypt(bytes.as_slice(), bytes.as_slice()); ctr_state.decrypt(bytes.as_slice(), bytes.as_slice());
} }
if (!encryption_key_.empty()) { if (encryption_key_.is_secret()) {
CHECK(next_part_ == part.id) << tag("expected part.id", next_part_) << "!=" << tag("part.id", part.id); CHECK(next_part_ == part.id) << tag("expected part.id", next_part_) << "!=" << tag("part.id", part.id);
CHECK(!next_part_stop_); CHECK(!next_part_stop_);
next_part_++; next_part_++;
@ -332,10 +347,10 @@ void FileDownloader::on_progress(int32 part_count, int32 part_size, int32 ready_
if (ready_size == 0 || path_.empty()) { if (ready_size == 0 || path_.empty()) {
return; return;
} }
if (encryption_key_.empty()) { if (encryption_key_.empty() || encryption_key_.is_secure()) {
callback_->on_partial_download(PartialLocalFileLocation{remote_.file_type_, path_, part_size, ready_part_count, ""}, callback_->on_partial_download(PartialLocalFileLocation{remote_.file_type_, path_, part_size, ready_part_count, ""},
ready_size); ready_size);
} else { } else if (encryption_key_.is_secret()) {
UInt256 iv; UInt256 iv;
if (ready_part_count == next_part_) { if (ready_part_count == next_part_) {
iv = encryption_key_.mutable_iv(); iv = encryption_key_.mutable_iv();
@ -345,6 +360,8 @@ void FileDownloader::on_progress(int32 part_count, int32 part_size, int32 ready_
callback_->on_partial_download(PartialLocalFileLocation{remote_.file_type_, path_, part_size, ready_part_count, callback_->on_partial_download(PartialLocalFileLocation{remote_.file_type_, path_, part_size, ready_part_count,
Slice(iv.raw, sizeof(iv)).str()}, Slice(iv.raw, sizeof(iv)).str()},
ready_size); ready_size);
} else {
UNREACHABLE();
} }
} }

View File

@ -181,6 +181,17 @@ void FileLoadManager::on_partial_download(const PartialLocalFileLocation &partia
} }
} }
void FileLoadManager::on_hash(string hash) {
auto node_id = get_link_token();
auto node = nodes_container_.get(node_id);
if (node == nullptr) {
return;
}
if (!stop_flag_) {
send_closure(callback_, &Callback::on_hash, node->query_id_, std::move(hash));
}
}
void FileLoadManager::on_partial_upload(const PartialRemoteFileLocation &partial_remote, int64 ready_size) { void FileLoadManager::on_partial_upload(const PartialRemoteFileLocation &partial_remote, int64 ready_size) {
auto node_id = get_link_token(); auto node_id = get_link_token();
auto node = nodes_container_.get(node_id); auto node = nodes_container_.get(node_id);

View File

@ -36,6 +36,7 @@ class FileLoadManager final : public Actor {
virtual void on_start_download(QueryId id) = 0; virtual void on_start_download(QueryId id) = 0;
virtual void on_partial_download(QueryId id, const PartialLocalFileLocation &partial_local, int64 ready_size) = 0; virtual void on_partial_download(QueryId id, const PartialLocalFileLocation &partial_local, int64 ready_size) = 0;
virtual void on_partial_upload(QueryId id, const PartialRemoteFileLocation &partial_remote, int64 ready_size) = 0; virtual void on_partial_upload(QueryId id, const PartialRemoteFileLocation &partial_remote, int64 ready_size) = 0;
virtual void on_hash(QueryId id, string hash) = 0;
virtual void on_upload_ok(QueryId id, FileType file_type, const PartialRemoteFileLocation &remtoe, int64 size) = 0; virtual void on_upload_ok(QueryId id, FileType file_type, const PartialRemoteFileLocation &remtoe, int64 size) = 0;
virtual void on_upload_full_ok(QueryId id, const FullRemoteFileLocation &remote) = 0; virtual void on_upload_full_ok(QueryId id, const FullRemoteFileLocation &remote) = 0;
virtual void on_download_ok(QueryId id, const FullLocalFileLocation &local, int64 size) = 0; virtual void on_download_ok(QueryId id, const FullLocalFileLocation &local, int64 size) = 0;
@ -85,6 +86,7 @@ class FileLoadManager final : public Actor {
void on_start_download(); void on_start_download();
void on_partial_download(const PartialLocalFileLocation &partial_local, int64 ready_size); void on_partial_download(const PartialLocalFileLocation &partial_local, int64 ready_size);
void on_partial_upload(const PartialRemoteFileLocation &partial_remote, int64 ready_size); void on_partial_upload(const PartialRemoteFileLocation &partial_remote, int64 ready_size);
void on_hash(string hash);
void on_ok_download(const FullLocalFileLocation &local, int64 size); void on_ok_download(const FullLocalFileLocation &local, int64 size);
void on_ok_upload(FileType file_type, const PartialRemoteFileLocation &remote, int64 size); void on_ok_upload(FileType file_type, const PartialRemoteFileLocation &remote, int64 size);
void on_ok_upload_full(const FullRemoteFileLocation &remote); void on_ok_upload_full(const FullRemoteFileLocation &remote);
@ -121,6 +123,9 @@ class FileLoadManager final : public Actor {
private: private:
ActorShared<FileLoadManager> actor_id_; ActorShared<FileLoadManager> actor_id_;
void on_hash(string hash) override {
send_closure(actor_id_, &FileLoadManager::on_hash, std::move(hash));
}
void on_partial_upload(const PartialRemoteFileLocation &partial_remote, int64 ready_size) override { void on_partial_upload(const PartialRemoteFileLocation &partial_remote, int64 ready_size) override {
send_closure(actor_id_, &FileLoadManager::on_partial_upload, partial_remote, ready_size); send_closure(actor_id_, &FileLoadManager::on_partial_upload, partial_remote, ready_size);
} }

View File

@ -10,6 +10,7 @@
#include "td/telegram/telegram_api.h" #include "td/telegram/telegram_api.h"
#include "td/telegram/DialogId.h" #include "td/telegram/DialogId.h"
#include "td/telegram/SecureStorage.h"
#include "td/telegram/net/DcId.h" #include "td/telegram/net/DcId.h"
#include "td/utils/buffer.h" #include "td/utils/buffer.h"
@ -157,42 +158,84 @@ constexpr int32 file_type_size = static_cast<int32>(FileType::Size);
extern const char *file_type_name[file_type_size]; extern const char *file_type_name[file_type_size];
struct FileEncryptionKey { struct FileEncryptionKey {
enum class Type { None, Secret, Secure };
FileEncryptionKey() = default; FileEncryptionKey() = default;
FileEncryptionKey(Slice key, Slice iv) : key_iv_(key.size() + iv.size(), '\0') { FileEncryptionKey(Slice key, Slice iv) : key_iv_(key.size() + iv.size(), '\0'), type_(Type::Secret) {
if (key.size() != 32 || iv.size() != 32) { if (key.size() != 32 || iv.size() != 32) {
LOG(ERROR) << "Wrong key/iv sizes: " << key.size() << " " << iv.size(); LOG(ERROR) << "Wrong key/iv sizes: " << key.size() << " " << iv.size();
type_ = Type::None;
return; return;
} }
CHECK(key_iv_.size() == 64); CHECK(key_iv_.size() == 64);
std::memcpy(&key_iv_[0], key.data(), key.size()); MutableSlice(key_iv_).copy_from(key);
std::memcpy(&key_iv_[key.size()], iv.data(), iv.size()); MutableSlice(key_iv_).remove_suffix(key.size()).copy_from(iv);
} }
FileEncryptionKey(const secure_storage::Secret &secret) : type_(Type::Secure) {
key_iv_ = secret.as_slice().str();
}
bool is_secret() const {
return type_ == Type::Secret;
}
bool is_secure() const {
return type_ == Type::Secure;
}
static FileEncryptionKey create() { static FileEncryptionKey create() {
FileEncryptionKey res; FileEncryptionKey res;
res.key_iv_.resize(64); res.key_iv_.resize(64);
Random::secure_bytes(res.key_iv_); Random::secure_bytes(res.key_iv_);
res.type_ = Type::Secret;
return res; return res;
} }
static FileEncryptionKey create_secure_key() {
return FileEncryptionKey(secure_storage::Secret::create_new());
}
const UInt256 &key() const { const UInt256 &key() const {
CHECK(is_secret());
CHECK(key_iv_.size() == 64); CHECK(key_iv_.size() == 64);
return *reinterpret_cast<const UInt256 *>(key_iv_.data()); return *reinterpret_cast<const UInt256 *>(key_iv_.data());
} }
Slice key_slice() const { Slice key_slice() const {
CHECK(is_secret());
CHECK(key_iv_.size() == 64); CHECK(key_iv_.size() == 64);
return Slice(key_iv_.data(), 32); return Slice(key_iv_.data(), 32);
} }
secure_storage::Secret secret() const {
CHECK(is_secure());
return secure_storage::Secret::create(Slice(key_iv_).truncate(32)).move_as_ok();
}
bool has_value_hash() const {
CHECK(is_secure());
return key_iv_.size() > secure_storage::Secret::size();
}
void set_value_hash(const secure_storage::ValueHash &value_hash) {
key_iv_.resize(secure_storage::Secret::size() + value_hash.as_slice().size());
MutableSlice(key_iv_).remove_prefix(secure_storage::Secret::size()).copy_from(value_hash.as_slice());
}
secure_storage::ValueHash value_hash() const {
CHECK(has_value_hash());
return secure_storage::ValueHash::create(Slice(key_iv_).remove_prefix(secure_storage::Secret::size())).move_as_ok();
}
UInt256 &mutable_iv() { UInt256 &mutable_iv() {
CHECK(is_secret());
CHECK(key_iv_.size() == 64); CHECK(key_iv_.size() == 64);
return *reinterpret_cast<UInt256 *>(&key_iv_[0] + 32); return *reinterpret_cast<UInt256 *>(&key_iv_[0] + 32);
} }
Slice iv_slice() const { Slice iv_slice() const {
CHECK(is_secret());
CHECK(key_iv_.size() == 64); CHECK(key_iv_.size() == 64);
return Slice(key_iv_.data() + 32, 32); return Slice(key_iv_.data() + 32, 32);
} }
int32 calc_fingerprint() const { int32 calc_fingerprint() const {
CHECK(is_secret());
char buf[16]; char buf[16];
md5(key_iv_, {buf, sizeof(buf)}); md5(key_iv_, {buf, sizeof(buf)});
return as<int32>(buf) ^ as<int32>(buf + 4); return as<int32>(buf) ^ as<int32>(buf + 4);
@ -207,11 +250,17 @@ struct FileEncryptionKey {
td::store(key_iv_, storer); td::store(key_iv_, storer);
} }
template <class ParserT> template <class ParserT>
void parse(ParserT &parser) { void parse(Type type, ParserT &parser) {
td::parse(key_iv_, parser); td::parse(key_iv_, parser);
if (key_iv_.empty()) {
type_ = Type::None;
} else {
type_ = type;
}
} }
string key_iv_; // TODO wrong alignment is possible string key_iv_; // TODO wrong alignment is possible
Type type_;
}; };
inline bool operator==(const FileEncryptionKey &lhs, const FileEncryptionKey &rhs) { inline bool operator==(const FileEncryptionKey &lhs, const FileEncryptionKey &rhs) {
@ -1141,9 +1190,11 @@ class FileData {
using ::td::store; using ::td::store;
bool has_owner_dialog_id = owner_dialog_id_.is_valid(); bool has_owner_dialog_id = owner_dialog_id_.is_valid();
bool has_expected_size = size_ == 0 && expected_size_ != 0; bool has_expected_size = size_ == 0 && expected_size_ != 0;
bool encryption_key_is_secure = encryption_key_.is_secure();
BEGIN_STORE_FLAGS(); BEGIN_STORE_FLAGS();
STORE_FLAG(has_owner_dialog_id); STORE_FLAG(has_owner_dialog_id);
STORE_FLAG(has_expected_size); STORE_FLAG(has_expected_size);
STORE_FLAG(encryption_key_is_secure);
END_STORE_FLAGS(); END_STORE_FLAGS();
if (has_owner_dialog_id) { if (has_owner_dialog_id) {
@ -1168,9 +1219,11 @@ class FileData {
using ::td::parse; using ::td::parse;
bool has_owner_dialog_id; bool has_owner_dialog_id;
bool has_expected_size; bool has_expected_size;
bool encryption_key_is_secure;
BEGIN_PARSE_FLAGS(); BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_owner_dialog_id); PARSE_FLAG(has_owner_dialog_id);
PARSE_FLAG(has_expected_size); PARSE_FLAG(has_expected_size);
PARSE_FLAG(encryption_key_is_secure);
END_PARSE_FLAGS(); END_PARSE_FLAGS();
if (has_owner_dialog_id) { if (has_owner_dialog_id) {
@ -1193,7 +1246,8 @@ class FileData {
} }
parse(remote_name_, parser); parse(remote_name_, parser);
parse(url_, parser); parse(url_, parser);
parse(encryption_key_, parser); encryption_key_.parse(encryption_key_is_secure ? FileEncryptionKey::Type::Secure : FileEncryptionKey::Type::Secret,
parser);
} }
}; };
inline StringBuilder &operator<<(StringBuilder &sb, const FileData &file_data) { inline StringBuilder &operator<<(StringBuilder &sb, const FileData &file_data) {

View File

@ -300,8 +300,13 @@ int64 FileView::local_size() const {
switch (node_->local_.type()) { switch (node_->local_.type()) {
case LocalFileLocation::Type::Full: case LocalFileLocation::Type::Full:
return node_->size_; return node_->size_;
case LocalFileLocation::Type::Partial: case LocalFileLocation::Type::Partial: {
if (is_encrypted_secure()) {
// File is not decrypted yet
return 0;
}
return node_->local_.partial().part_size_ * node_->local_.partial().ready_part_count_; return node_->local_.partial().part_size_ * node_->local_.partial().ready_part_count_;
}
default: default:
return 0; return 0;
} }
@ -509,6 +514,8 @@ string FileManager::get_file_name(FileType file_type, Slice path) {
case FileType::Temp: case FileType::Temp:
case FileType::EncryptedThumbnail: case FileType::EncryptedThumbnail:
case FileType::Wallpaper: case FileType::Wallpaper:
case FileType::Secure:
case FileType::SecureRaw:
break; break;
default: default:
UNREACHABLE(); UNREACHABLE();
@ -1213,6 +1220,9 @@ void FileManager::flush_to_pmc(FileNodePtr node, bool new_remote, bool new_local
data.local_ = LocalFileLocation(); data.local_ = LocalFileLocation();
data.remote_ = RemoteFileLocation(); data.remote_ = RemoteFileLocation();
} }
if (data.remote_.type() != RemoteFileLocation::Type::Full && node->encryption_key_.is_secure()) {
data.remote_ = RemoteFileLocation();
}
data.size_ = node->size_; data.size_ = node->size_;
data.expected_size_ = node->expected_size_; data.expected_size_ = node->expected_size_;
@ -1670,6 +1680,10 @@ void FileManager::run_upload(FileNodePtr node, std::vector<int> bad_parts) {
if (node->get_by_hash_ || node->generate_id_ == 0 || !node->generate_was_update_) { if (node->get_by_hash_ || node->generate_id_ == 0 || !node->generate_was_update_) {
return; return;
} }
if (file_view.has_generate_location() && file_view.generate_location().file_type_ == FileType::Secure) {
// Can't upload secure file before its size is known.
return;
}
} }
int8 priority = 0; int8 priority = 0;
FileId file_id = node->main_file_id_; FileId file_id = node->main_file_id_;
@ -1701,6 +1715,14 @@ void FileManager::run_upload(FileNodePtr node, std::vector<int> bad_parts) {
LOG_IF(FATAL, !success) << "Failed to set encryption key for file " << file_id; LOG_IF(FATAL, !success) << "Failed to set encryption key for file " << file_id;
} }
// create encryption key if necessary
if (file_view.has_local_location() && file_view.local_location().file_type_ == FileType::Secure &&
file_view.encryption_key().empty()) {
CHECK(!node->file_ids_.empty());
bool success = set_encryption_key(node->file_ids_[0], FileEncryptionKey::create_secure_key());
LOG_IF(FATAL, !success) << "Failed to set encryption key for file " << file_id;
}
if (old_priority != 0) { if (old_priority != 0) {
LOG(INFO) << "File " << file_id << " is already uploading"; LOG(INFO) << "File " << file_id << " is already uploading";
CHECK(node->upload_id_ != 0); CHECK(node->upload_id_ != 0);
@ -1894,7 +1916,7 @@ vector<tl_object_ptr<td_api::file>> FileManager::get_files_object(const vector<F
} }
Result<FileId> FileManager::check_input_file_id(FileType type, Result<FileId> result, bool is_encrypted, Result<FileId> FileManager::check_input_file_id(FileType type, Result<FileId> result, bool is_encrypted,
bool allow_zero) { bool allow_zero, bool is_secure) {
TRY_RESULT(file_id, std::move(result)); TRY_RESULT(file_id, std::move(result));
if (allow_zero && !file_id.is_valid()) { if (allow_zero && !file_id.is_valid()) {
return FileId(); return FileId();
@ -1906,7 +1928,7 @@ Result<FileId> FileManager::check_input_file_id(FileType type, Result<FileId> re
} }
auto file_view = FileView(file_node); auto file_view = FileView(file_node);
FileType real_type = file_view.get_type(); FileType real_type = file_view.get_type();
if (!is_encrypted) { if (!is_encrypted && !is_secure) {
if (real_type != type && !(real_type == FileType::Temp && file_view.has_url()) && if (real_type != type && !(real_type == FileType::Temp && file_view.has_url()) &&
!(is_document_type(real_type) && is_document_type(type))) { !(is_document_type(real_type) && is_document_type(type))) {
// TODO: send encrypted file to unencrypted chat // TODO: send encrypted file to unencrypted chat
@ -1953,7 +1975,7 @@ Result<FileId> FileManager::get_input_thumbnail_file_id(const tl_object_ptr<td_a
Result<FileId> FileManager::get_input_file_id(FileType type, const tl_object_ptr<td_api::InputFile> &file, Result<FileId> FileManager::get_input_file_id(FileType type, const tl_object_ptr<td_api::InputFile> &file,
DialogId owner_dialog_id, bool allow_zero, bool is_encrypted, DialogId owner_dialog_id, bool allow_zero, bool is_encrypted,
bool get_by_hash) { bool get_by_hash, bool is_secure) {
if (is_encrypted) { if (is_encrypted) {
get_by_hash = false; get_by_hash = false;
} }
@ -1964,6 +1986,8 @@ Result<FileId> FileManager::get_input_file_id(FileType type, const tl_object_ptr
return Status::Error(6, "InputFile not specified"); return Status::Error(6, "InputFile not specified");
} }
auto new_type = is_encrypted ? FileType::Encrypted : (is_secure ? FileType::Secure : type);
auto r_file_id = [&]() -> Result<FileId> { auto r_file_id = [&]() -> Result<FileId> {
switch (file->get_id()) { switch (file->get_id()) {
case td_api::inputFileLocal::ID: { case td_api::inputFileLocal::ID: {
@ -1971,8 +1995,7 @@ Result<FileId> FileManager::get_input_file_id(FileType type, const tl_object_ptr
if (allow_zero && path.empty()) { if (allow_zero && path.empty()) {
return FileId(); return FileId();
} }
return register_local(FullLocalFileLocation(is_encrypted ? FileType::Encrypted : type, path, 0), return register_local(FullLocalFileLocation(new_type, path, 0), owner_dialog_id, 0, get_by_hash);
owner_dialog_id, 0, get_by_hash);
} }
case td_api::inputFileId::ID: { case td_api::inputFileId::ID: {
FileId file_id(static_cast<const td_api::inputFileId *>(file.get())->id_, 0); FileId file_id(static_cast<const td_api::inputFileId *>(file.get())->id_, 0);
@ -1990,9 +2013,8 @@ Result<FileId> FileManager::get_input_file_id(FileType type, const tl_object_ptr
} }
case td_api::inputFileGenerated::ID: { case td_api::inputFileGenerated::ID: {
auto *generated_file = static_cast<const td_api::inputFileGenerated *>(file.get()); auto *generated_file = static_cast<const td_api::inputFileGenerated *>(file.get());
return register_generate(is_encrypted ? FileType::Encrypted : type, FileLocationSource::FromUser, return register_generate(new_type, FileLocationSource::FromUser, generated_file->original_path_,
generated_file->original_path_, generated_file->conversion_, owner_dialog_id, generated_file->conversion_, owner_dialog_id, generated_file->expected_size_);
generated_file->expected_size_);
} }
default: default:
UNREACHABLE(); UNREACHABLE();
@ -2000,7 +2022,7 @@ Result<FileId> FileManager::get_input_file_id(FileType type, const tl_object_ptr
} }
}(); }();
return check_input_file_id(type, std::move(r_file_id), is_encrypted, allow_zero); return check_input_file_id(type, std::move(r_file_id), is_encrypted, allow_zero, is_secure);
} }
vector<tl_object_ptr<telegram_api::InputDocument>> FileManager::get_input_documents(const vector<FileId> &file_ids) { vector<tl_object_ptr<telegram_api::InputDocument>> FileManager::get_input_documents(const vector<FileId> &file_ids) {
@ -2069,6 +2091,23 @@ void FileManager::on_partial_download(QueryId query_id, const PartialLocalFileLo
try_flush_node(file_node); try_flush_node(file_node);
} }
void FileManager::on_hash(QueryId query_id, string hash) {
auto query = queries_container_.get(query_id);
CHECK(query != nullptr);
auto file_id = query->file_id_;
auto file_node = get_file_node(file_id);
if (!file_node) {
return;
}
if (file_node->upload_id_ != query_id) {
return;
}
file_node->encryption_key_.set_value_hash(secure_storage::ValueHash::create(hash).move_as_ok());
}
void FileManager::on_partial_upload(QueryId query_id, const PartialRemoteFileLocation &partial_remote, void FileManager::on_partial_upload(QueryId query_id, const PartialRemoteFileLocation &partial_remote,
int64 ready_size) { int64 ready_size) {
auto query = queries_container_.get(query_id); auto query = queries_container_.get(query_id);

View File

@ -345,7 +345,7 @@ class FileManager : public FileLoadManager::Callback {
DialogId owner_dialog_id, bool is_encrypted) TD_WARN_UNUSED_RESULT; DialogId owner_dialog_id, bool is_encrypted) TD_WARN_UNUSED_RESULT;
Result<FileId> get_input_file_id(FileType type, const tl_object_ptr<td_api::InputFile> &file, Result<FileId> get_input_file_id(FileType type, const tl_object_ptr<td_api::InputFile> &file,
DialogId owner_dialog_id, bool allow_zero, bool is_encrypted, DialogId owner_dialog_id, bool allow_zero, bool is_encrypted,
bool get_by_hash = false) TD_WARN_UNUSED_RESULT; bool get_by_hash = false, bool is_secure = false) TD_WARN_UNUSED_RESULT;
vector<tl_object_ptr<telegram_api::InputDocument>> get_input_documents(const vector<FileId> &file_ids); vector<tl_object_ptr<telegram_api::InputDocument>> get_input_documents(const vector<FileId> &file_ids);
@ -356,8 +356,8 @@ class FileManager : public FileLoadManager::Callback {
FileId parse_file(T &parser); FileId parse_file(T &parser);
private: private:
Result<FileId> check_input_file_id(FileType type, Result<FileId> result, bool is_encrypted, Result<FileId> check_input_file_id(FileType type, Result<FileId> result, bool is_encrypted, bool allow_zero,
bool allow_zero) TD_WARN_UNUSED_RESULT; bool is_secure) TD_WARN_UNUSED_RESULT;
FileId register_url(string url, FileType file_type, FileLocationSource file_location_source, FileId register_url(string url, FileType file_type, FileLocationSource file_location_source,
DialogId owner_dialog_id); DialogId owner_dialog_id);
@ -465,6 +465,7 @@ class FileManager : public FileLoadManager::Callback {
void on_start_download(QueryId query_id) override; void on_start_download(QueryId query_id) override;
void on_partial_download(QueryId query_id, const PartialLocalFileLocation &partial_local, int64 ready_size) override; void on_partial_download(QueryId query_id, const PartialLocalFileLocation &partial_local, int64 ready_size) override;
void on_hash(QueryId query_id, string hash) override;
void on_partial_upload(QueryId query_id, const PartialRemoteFileLocation &partial_remote, int64 ready_size) override; void on_partial_upload(QueryId query_id, const PartialRemoteFileLocation &partial_remote, int64 ready_size) override;
void on_download_ok(QueryId query_id, const FullLocalFileLocation &local, int64 size) override; void on_download_ok(QueryId query_id, const FullLocalFileLocation &local, int64 size) override;
void on_upload_ok(QueryId query_id, FileType file_type, const PartialRemoteFileLocation &partial_remote, void on_upload_ok(QueryId query_id, FileType file_type, const PartialRemoteFileLocation &partial_remote,

View File

@ -39,11 +39,14 @@ void FileManager::store_file(FileId file_id, StorerT &storer, int32 ttl) const {
bool has_encryption_key = false; bool has_encryption_key = false;
bool has_expected_size = bool has_expected_size =
file_store_type == FileStoreType::Remote && file_view.size() == 0 && file_view.expected_size() != 0; file_store_type == FileStoreType::Remote && file_view.size() == 0 && file_view.expected_size() != 0;
bool has_secure_key = false;
if (file_store_type != FileStoreType::Empty) { if (file_store_type != FileStoreType::Empty) {
has_encryption_key = !file_view.empty() && file_view.is_encrypted_secret(); has_encryption_key = !file_view.empty() && file_view.is_encrypted_secret();
has_secure_key = !file_view.empty() && file_view.is_encrypted_secure();
BEGIN_STORE_FLAGS(); BEGIN_STORE_FLAGS();
STORE_FLAG(has_encryption_key); STORE_FLAG(has_encryption_key);
STORE_FLAG(has_expected_size); STORE_FLAG(has_expected_size);
STORE_FLAG(has_secure_key);
END_STORE_FLAGS(); END_STORE_FLAGS();
} }
@ -99,6 +102,8 @@ void FileManager::store_file(FileId file_id, StorerT &storer, int32 ttl) const {
} }
if (has_encryption_key) { if (has_encryption_key) {
store(file_view.encryption_key(), storer); store(file_view.encryption_key(), storer);
} else if (has_secure_key) {
store(file_view.encryption_key(), storer);
} }
} }
@ -113,11 +118,13 @@ FileId FileManager::parse_file(ParserT &parser) {
bool has_encryption_key = false; bool has_encryption_key = false;
bool has_expected_size = false; bool has_expected_size = false;
bool has_secure_key = false;
if (file_store_type != FileStoreType::Empty) { if (file_store_type != FileStoreType::Empty) {
if (parser.version() >= static_cast<int32>(Version::StoreFileEncryptionKey)) { if (parser.version() >= static_cast<int32>(Version::StoreFileEncryptionKey)) {
BEGIN_PARSE_FLAGS(); BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_encryption_key); PARSE_FLAG(has_encryption_key);
PARSE_FLAG(has_expected_size); PARSE_FLAG(has_expected_size);
PARSE_FLAG(has_secure_key);
END_PARSE_FLAGS(); END_PARSE_FLAGS();
} }
} }
@ -212,7 +219,11 @@ FileId FileManager::parse_file(ParserT &parser) {
if (has_encryption_key) { if (has_encryption_key) {
FileEncryptionKey encryption_key; FileEncryptionKey encryption_key;
parse(encryption_key, parser); encryption_key.parse(FileEncryptionKey::Type::Secret, parser);
set_encryption_key(file_id, std::move(encryption_key));
} else if (has_secure_key) {
FileEncryptionKey encryption_key;
encryption_key.parse(FileEncryptionKey::Type::Secure, parser);
set_encryption_key(file_id, std::move(encryption_key)); set_encryption_key(file_id, std::move(encryption_key));
} }

View File

@ -8,6 +8,8 @@
#include "td/telegram/telegram_api.h" #include "td/telegram/telegram_api.h"
#include "td/telegram/files/FileLoaderUtils.h"
#include "td/telegram/Global.h" #include "td/telegram/Global.h"
#include "td/telegram/net/NetQueryDispatcher.h" #include "td/telegram/net/NetQueryDispatcher.h"
@ -16,6 +18,7 @@
#include "td/utils/format.h" #include "td/utils/format.h"
#include "td/utils/logging.h" #include "td/utils/logging.h"
#include "td/utils/misc.h" #include "td/utils/misc.h"
#include "td/utils/port/path.h"
#include "td/utils/Random.h" #include "td/utils/Random.h"
#include "td/utils/ScopeGuard.h" #include "td/utils/ScopeGuard.h"
@ -29,7 +32,7 @@ FileUploader::FileUploader(const LocalFileLocation &local, const RemoteFileLocat
, encryption_key_(encryption_key) , encryption_key_(encryption_key)
, bad_parts_(std::move(bad_parts)) , bad_parts_(std::move(bad_parts))
, callback_(std::move(callback)) { , callback_(std::move(callback)) {
if (!encryption_key_.empty()) { if (encryption_key_.is_secret()) {
iv_ = encryption_key_.mutable_iv(); iv_ = encryption_key_.mutable_iv();
generate_iv_ = encryption_key_.iv_slice().str(); generate_iv_ = encryption_key_.iv_slice().str();
} }
@ -83,11 +86,17 @@ Result<FileLoader::PrefixInfo> FileUploader::on_update_local_location(const Loca
SCOPE_EXIT { SCOPE_EXIT {
try_release_fd(); try_release_fd();
}; };
if (encryption_key_.is_secure() && !fd_path_.empty()) {
return Status::Error("Can't change local location for Secure file");
}
string path; string path;
int64 local_size = 0; int64 local_size = 0;
bool local_is_ready{false}; bool local_is_ready{false};
FileType file_type{FileType::Temp}; FileType file_type{FileType::Temp};
if (location.type() == LocalFileLocation::Type::Empty) { if (location.type() == LocalFileLocation::Type::Empty ||
(location.type() == LocalFileLocation::Type::Partial && encryption_key_.is_secure())) {
path = ""; path = "";
local_size = 0; local_size = 0;
local_is_ready = false; local_is_ready = false;
@ -103,6 +112,18 @@ Result<FileLoader::PrefixInfo> FileUploader::on_update_local_location(const Loca
file_type = location.full().file_type_; file_type = location.full().file_type_;
} }
bool is_temp = false;
if (encryption_key_.is_secure() && local_is_ready) {
TRY_RESULT(file_fd_path, open_temp_file(FileType::Temp));
file_fd_path.first.close();
auto new_path = std::move(file_fd_path.second);
TRY_RESULT(hash, secure_storage::encrypt_file(encryption_key_.secret(), path, new_path));
LOG(ERROR) << "ENCRYPT " << path << " " << new_path;
callback_->on_hash(hash.as_slice().str());
path = new_path;
is_temp = true;
}
if (!path.empty() && path != fd_path_) { if (!path.empty() && path != fd_path_) {
auto res_fd = FileFd::open(path, FileFd::Read); auto res_fd = FileFd::open(path, FileFd::Read);
@ -120,6 +141,7 @@ Result<FileLoader::PrefixInfo> FileUploader::on_update_local_location(const Loca
fd_.close(); fd_.close();
fd_ = res_fd.move_as_ok(); fd_ = res_fd.move_as_ok();
fd_path_ = path; fd_path_ = path;
is_temp_ = is_temp;
} }
if (local_is_ready) { if (local_is_ready) {
@ -154,10 +176,18 @@ Result<FileLoader::PrefixInfo> FileUploader::on_update_local_location(const Loca
Status FileUploader::on_ok(int64 size) { Status FileUploader::on_ok(int64 size) {
fd_.close(); fd_.close();
if (is_temp_) {
LOG(ERROR) << "UNLINK " << fd_path_;
unlink(fd_path_).ignore();
}
return Status::OK(); return Status::OK();
} }
void FileUploader::on_error(Status status) { void FileUploader::on_error(Status status) {
fd_.close(); fd_.close();
if (is_temp_) {
LOG(ERROR) << "UNLINK " << fd_path_;
unlink(fd_path_).ignore();
}
callback_->on_error(std::move(status)); callback_->on_error(std::move(status));
} }
@ -196,12 +226,12 @@ void FileUploader::after_start_parts() {
Result<std::pair<NetQueryPtr, bool>> FileUploader::start_part(Part part, int32 part_count) { Result<std::pair<NetQueryPtr, bool>> FileUploader::start_part(Part part, int32 part_count) {
auto padded_size = part.size; auto padded_size = part.size;
if (!encryption_key_.empty()) { if (encryption_key_.is_secret()) {
padded_size = (padded_size + 15) & ~15; padded_size = (padded_size + 15) & ~15;
} }
BufferSlice bytes(padded_size); BufferSlice bytes(padded_size);
TRY_RESULT(size, fd_.pread(bytes.as_slice().truncate(part.size), part.offset)); TRY_RESULT(size, fd_.pread(bytes.as_slice().truncate(part.size), part.offset));
if (!encryption_key_.empty()) { if (encryption_key_.is_secret()) {
Random::secure_bytes(bytes.as_slice().substr(part.size)); Random::secure_bytes(bytes.as_slice().substr(part.size));
if (next_offset_ == part.offset) { if (next_offset_ == part.offset) {
aes_ige_encrypt(encryption_key_.key(), &iv_, bytes.as_slice(), bytes.as_slice()); aes_ige_encrypt(encryption_key_.key(), &iv_, bytes.as_slice(), bytes.as_slice());

View File

@ -22,6 +22,7 @@ class FileUploader : public FileLoader {
public: public:
class Callback : public FileLoader::Callback { class Callback : public FileLoader::Callback {
public: public:
virtual void on_hash(string hash) = 0;
virtual void on_partial_upload(const PartialRemoteFileLocation &partial_remote, int64 ready_size) = 0; virtual void on_partial_upload(const PartialRemoteFileLocation &partial_remote, int64 ready_size) = 0;
virtual void on_ok(FileType file_type, const PartialRemoteFileLocation &partial_remote, int64 size) = 0; virtual void on_ok(FileType file_type, const PartialRemoteFileLocation &partial_remote, int64 size) = 0;
virtual void on_error(Status status) = 0; virtual void on_error(Status status) = 0;
@ -52,6 +53,7 @@ class FileUploader : public FileLoader {
FileFd fd_; FileFd fd_;
string fd_path_; string fd_path_;
bool is_temp_ = false;
int64 file_id_; int64 file_id_;
bool big_flag_; bool big_flag_;