mirror of https://github.com/procxx/kepka.git
Update API scheme.
This commit is contained in:
parent
2bc60fa54f
commit
b0a9d26a94
|
@ -281,7 +281,7 @@ messageActionPhoneCall#80e11a7f flags:# call_id:long reason:flags.0?PhoneCallDis
|
||||||
messageActionScreenshotTaken#4792929b = MessageAction;
|
messageActionScreenshotTaken#4792929b = MessageAction;
|
||||||
messageActionCustomAction#fae69f56 message:string = MessageAction;
|
messageActionCustomAction#fae69f56 message:string = MessageAction;
|
||||||
messageActionBotAllowed#abe9affe domain:string = MessageAction;
|
messageActionBotAllowed#abe9affe domain:string = MessageAction;
|
||||||
messageActionSecureValuesSentMe#1b287353 values:Vector<SecureValue> credentials:SecureCredentialsEncrypted = MessageAction;
|
messageActionSecureValuesSentMe#9bc8ec4 values:Vector<SecureValue> credentials:SecureCredentialsEncrypted payload:bytes = MessageAction;
|
||||||
messageActionSecureValuesSent#d95c6154 types:Vector<SecureValueType> = MessageAction;
|
messageActionSecureValuesSent#d95c6154 types:Vector<SecureValueType> = MessageAction;
|
||||||
|
|
||||||
dialog#e4def5db flags:# pinned:flags.2?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog;
|
dialog#e4def5db flags:# pinned:flags.2?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog;
|
||||||
|
@ -589,9 +589,9 @@ account.authorizations#1250abde authorizations:Vector<Authorization> = account.A
|
||||||
account.noPassword#5ea182f6 new_salt:bytes new_secure_salt:bytes secure_random:bytes email_unconfirmed_pattern:string = account.Password;
|
account.noPassword#5ea182f6 new_salt:bytes new_secure_salt:bytes secure_random:bytes email_unconfirmed_pattern:string = account.Password;
|
||||||
account.password#d06c5fc3 current_salt:bytes new_salt:bytes new_secure_salt:bytes secure_random:bytes hint:string has_recovery:Bool email_unconfirmed_pattern:string = account.Password;
|
account.password#d06c5fc3 current_salt:bytes new_salt:bytes new_secure_salt:bytes secure_random:bytes hint:string has_recovery:Bool email_unconfirmed_pattern:string = account.Password;
|
||||||
|
|
||||||
account.passwordSettings#48ec1750 email:string secure_salt:bytes secure_secret:bytes secure_secret_hash:long = account.PasswordSettings;
|
account.passwordSettings#7bd9c3f1 email:string secure_salt:bytes secure_secret:bytes secure_secret_id:long = account.PasswordSettings;
|
||||||
|
|
||||||
account.passwordInputSettings#e553a6cf flags:# new_salt:flags.0?bytes new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_salt:flags.2?bytes new_secure_secret:flags.2?bytes new_secure_secret_hash:flags.2?long = account.PasswordInputSettings;
|
account.passwordInputSettings#21ffa60d flags:# new_salt:flags.0?bytes new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_salt:flags.2?bytes new_secure_secret:flags.2?bytes new_secure_secret_id:flags.2?long = account.PasswordInputSettings;
|
||||||
|
|
||||||
auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery;
|
auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery;
|
||||||
|
|
||||||
|
@ -967,28 +967,28 @@ help.proxyDataPromo#2bf7ee23 expires:int peer:Peer chats:Vector<Chat> users:Vect
|
||||||
help.termsOfServiceUpdateEmpty#e3309f7f expires:int = help.TermsOfServiceUpdate;
|
help.termsOfServiceUpdateEmpty#e3309f7f expires:int = help.TermsOfServiceUpdate;
|
||||||
help.termsOfServiceUpdate#28ecf961 expires:int terms_of_service:help.TermsOfService = help.TermsOfServiceUpdate;
|
help.termsOfServiceUpdate#28ecf961 expires:int terms_of_service:help.TermsOfService = help.TermsOfServiceUpdate;
|
||||||
|
|
||||||
inputSecureFileUploaded#c53ed020 id:long parts:int md5_checksum:string file_hash:bytes = InputSecureFile;
|
inputSecureFileUploaded#3334b0f0 id:long parts:int md5_checksum:string file_hash:bytes secret:bytes = InputSecureFile;
|
||||||
inputSecureFile#5367e5be id:long access_hash:long = InputSecureFile;
|
inputSecureFile#5367e5be id:long access_hash:long = InputSecureFile;
|
||||||
|
|
||||||
secureFileEmpty#64199744 = SecureFile;
|
secureFileEmpty#64199744 = SecureFile;
|
||||||
secureFile#40ad69ba id:long access_hash:long size:int dc_id:int file_hash:bytes = SecureFile;
|
secureFile#e0277a62 id:long access_hash:long size:int dc_id:int date:int file_hash:bytes secret:bytes = SecureFile;
|
||||||
|
|
||||||
secureData#262b0134 data:bytes data_hash:bytes = SecureData;
|
secureData#8aeabec3 data:bytes data_hash:bytes secret:bytes = SecureData;
|
||||||
|
|
||||||
secureValueVerified#612d86df date:int provider:string = SecureValueVerified;
|
secureValueVerified#2c8e61e2 date:int = SecureValueVerified;
|
||||||
|
|
||||||
secureValueTypeIdentity#37da58ca = SecureValueType;
|
secureValueTypeIdentity#37da58ca = SecureValueType;
|
||||||
secureValueTypeAddress#cbe31e26 = SecureValueType;
|
secureValueTypeAddress#cbe31e26 = SecureValueType;
|
||||||
secureValueTypePhone#b320aadb = SecureValueType;
|
secureValueTypePhone#b320aadb = SecureValueType;
|
||||||
secureValueTypeEmail#8e3ca7ee = SecureValueType;
|
secureValueTypeEmail#8e3ca7ee = SecureValueType;
|
||||||
|
|
||||||
secureValueIdentity#a6c927ad flags:# data:SecureData files:Vector<SecureFile> secret:bytes hash:bytes verified:flags.0?SecureValueVerified = SecureValue;
|
secureValueIdentity#4838ff84 flags:# data:SecureData files:Vector<SecureFile> hash:bytes verified:flags.0?SecureValueVerified = SecureValue;
|
||||||
secureValueAddress#74a0d79c flags:# data:SecureData files:Vector<SecureFile> secret:bytes hash:bytes verified:flags.0?SecureValueVerified = SecureValue;
|
secureValueAddress#2b9f6bef flags:# data:SecureData files:Vector<SecureFile> hash:bytes verified:flags.0?SecureValueVerified = SecureValue;
|
||||||
secureValuePhone#a1ca84fe flags:# phone:string hash:bytes verified:flags.0?SecureValueVerified = SecureValue;
|
secureValuePhone#a1ca84fe flags:# phone:string hash:bytes verified:flags.0?SecureValueVerified = SecureValue;
|
||||||
secureValueEmail#c4db6579 flags:# email:string hash:bytes verified:flags.0?SecureValueVerified = SecureValue;
|
secureValueEmail#c4db6579 flags:# email:string hash:bytes verified:flags.0?SecureValueVerified = SecureValue;
|
||||||
|
|
||||||
inputSecureValueIdentity#ae27d606 data:SecureData files:Vector<InputSecureFile> secret:bytes hash:bytes = InputSecureValue;
|
inputSecureValueIdentity#df93404 data:SecureData files:Vector<InputSecureFile> hash:bytes = InputSecureValue;
|
||||||
inputSecureValueAddress#c1f733e5 data:SecureData files:Vector<InputSecureFile> secret:bytes hash:bytes = InputSecureValue;
|
inputSecureValueAddress#5589502 data:SecureData files:Vector<InputSecureFile> hash:bytes = InputSecureValue;
|
||||||
inputSecureValuePhone#141e00b8 phone:string hash:bytes = InputSecureValue;
|
inputSecureValuePhone#141e00b8 phone:string hash:bytes = InputSecureValue;
|
||||||
inputSecureValueEmail#2dc15b9a email:string hash:bytes = InputSecureValue;
|
inputSecureValueEmail#2dc15b9a email:string hash:bytes = InputSecureValue;
|
||||||
|
|
||||||
|
@ -996,9 +996,9 @@ secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash;
|
||||||
|
|
||||||
secureValueSaved#c9147049 files:Vector<SecureFile> = SecureValueSaved;
|
secureValueSaved#c9147049 files:Vector<SecureFile> = SecureValueSaved;
|
||||||
|
|
||||||
secureCredentialsEncrypted#628fe12a data:bytes secret:bytes hash:bytes = SecureCredentialsEncrypted;
|
secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted;
|
||||||
|
|
||||||
account.authorizationForm#8d9dddeb flags:# accepted:flags.0?true required_types:Vector<SecureValueType> values:Vector<SecureValue> users:Vector<User> = account.AuthorizationForm;
|
account.authorizationForm#4cace8c4 required_types:Vector<SecureValueType> values:Vector<SecureValue> users:Vector<User> = account.AuthorizationForm;
|
||||||
|
|
||||||
account.sentEmailCode#28b1633b email_pattern:string = account.SentEmailCode;
|
account.sentEmailCode#28b1633b email_pattern:string = account.SentEmailCode;
|
||||||
|
|
||||||
|
@ -1058,10 +1058,10 @@ account.getWebAuthorizations#182e6d6f = account.WebAuthorizations;
|
||||||
account.resetWebAuthorization#2d01b9ef hash:long = Bool;
|
account.resetWebAuthorization#2d01b9ef hash:long = Bool;
|
||||||
account.resetWebAuthorizations#682d2594 = Bool;
|
account.resetWebAuthorizations#682d2594 = Bool;
|
||||||
account.getSecureValue#d97e77cb user_id:InputUser types:Vector<SecureValueType> = Vector<SecureValue>;
|
account.getSecureValue#d97e77cb user_id:InputUser types:Vector<SecureValueType> = Vector<SecureValue>;
|
||||||
account.saveSecureValue#842659a5 value:InputSecureValue secure_secret_hash:long = SecureValueSaved;
|
account.saveSecureValue#78969d0b value:InputSecureValue secure_secret_id:long = SecureValueSaved;
|
||||||
account.deleteSecureValue#b880bc4b types:Vector<SecureValueType> = Bool;
|
account.deleteSecureValue#b880bc4b types:Vector<SecureValueType> = Bool;
|
||||||
account.getAuthorizationForm#b86ba8e1 bot_id:int scope:string public_key:string = account.AuthorizationForm;
|
account.getAuthorizationForm#b86ba8e1 bot_id:int scope:string public_key:string = account.AuthorizationForm;
|
||||||
account.acceptAuthorization#e7027c94 bot_id:int scope:string public_key:string value_hashes:Vector<SecureValueHash> credentials:SecureCredentialsEncrypted = Bool;
|
account.acceptAuthorization#8d5e02e6 bot_id:int scope:string public_key:string value_hashes:Vector<SecureValueHash> credentials:SecureCredentialsEncrypted payload:bytes = Bool;
|
||||||
account.sendVerifyPhoneCode#823380b4 flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool = auth.SentCode;
|
account.sendVerifyPhoneCode#823380b4 flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool = auth.SentCode;
|
||||||
account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_code:string = Bool;
|
account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_code:string = Bool;
|
||||||
account.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode;
|
account.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode;
|
||||||
|
|
|
@ -494,10 +494,6 @@ int32 *hashSha256(const void *data, uint32 len, void *dest) {
|
||||||
return (int32*)SHA256((const uchar*)data, (size_t)len, (uchar*)dest);
|
return (int32*)SHA256((const uchar*)data, (size_t)len, (uchar*)dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 *hashSha512(const void *data, uint32 len, void *dest) {
|
|
||||||
return (int32*)SHA512((const uchar*)data, (size_t)len, (uchar*)dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
// md5 hash, taken somewhere from the internet
|
// md5 hash, taken somewhere from the internet
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -909,7 +909,7 @@ bool Messenger::openLocalUrl(const QString &url) {
|
||||||
auto params = url_parse_params(proxyMatch->captured(1), UrlParamNameTransform::ToLower);
|
auto params = url_parse_params(proxyMatch->captured(1), UrlParamNameTransform::ToLower);
|
||||||
ProxiesBoxController::ShowApplyConfirmation(ProxyData::Type::Mtproto, params);
|
ProxiesBoxController::ShowApplyConfirmation(ProxyData::Type::Mtproto, params);
|
||||||
return true;
|
return true;
|
||||||
} else if (auto authMatch = regex_match(qsl("^auth/?\\?(.+)(#|$)"), command, matchOptions)) {
|
} else if (auto authMatch = regex_match(qsl("^secureid/?\\?(.+)(#|$)"), command, matchOptions)) {
|
||||||
const auto params = url_parse_params(
|
const auto params = url_parse_params(
|
||||||
authMatch->captured(1),
|
authMatch->captured(1),
|
||||||
UrlParamNameTransform::ToLower);
|
UrlParamNameTransform::ToLower);
|
||||||
|
|
|
@ -143,11 +143,11 @@ void ScanButton::paintEvent(QPaintEvent *e) {
|
||||||
IdentityBox::IdentityBox(
|
IdentityBox::IdentityBox(
|
||||||
QWidget*,
|
QWidget*,
|
||||||
not_null<FormController*> controller,
|
not_null<FormController*> controller,
|
||||||
int fieldIndex,
|
int valueIndex,
|
||||||
const IdentityData &data,
|
const IdentityData &data,
|
||||||
std::vector<ScanInfo> &&files)
|
std::vector<ScanInfo> &&files)
|
||||||
: _controller(controller)
|
: _controller(controller)
|
||||||
, _fieldIndex(fieldIndex)
|
, _valueIndex(valueIndex)
|
||||||
, _files(std::move(files))
|
, _files(std::move(files))
|
||||||
, _uploadScan(this, "Upload scans") // #TODO langs
|
, _uploadScan(this, "Upload scans") // #TODO langs
|
||||||
, _name(
|
, _name(
|
||||||
|
@ -175,7 +175,7 @@ void IdentityBox::prepare() {
|
||||||
_scans.back()->resizeToWidth(st::boxWideWidth);
|
_scans.back()->resizeToWidth(st::boxWideWidth);
|
||||||
_scans.back()->deleteClicks(
|
_scans.back()->deleteClicks(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
_controller->deleteScan(_fieldIndex, index - 1);
|
_controller->deleteScan(_valueIndex, index - 1);
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,14 +294,14 @@ void IdentityBox::encryptScan(const QString &path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void IdentityBox::encryptScanContent(QByteArray &&content) {
|
void IdentityBox::encryptScanContent(QByteArray &&content) {
|
||||||
_controller->uploadScan(_fieldIndex, std::move(content));
|
_controller->uploadScan(_valueIndex, std::move(content));
|
||||||
}
|
}
|
||||||
|
|
||||||
void IdentityBox::save() {
|
void IdentityBox::save() {
|
||||||
auto data = IdentityData();
|
auto data = IdentityData();
|
||||||
data.name = _name->getLastText();
|
data.name = _name->getLastText();
|
||||||
data.surname = _surname->getLastText();
|
data.surname = _surname->getLastText();
|
||||||
_controller->saveFieldIdentity(_fieldIndex, data);
|
_controller->saveValueIdentity(_valueIndex, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Passport
|
} // namespace Passport
|
||||||
|
|
|
@ -30,7 +30,7 @@ public:
|
||||||
IdentityBox(
|
IdentityBox(
|
||||||
QWidget*,
|
QWidget*,
|
||||||
not_null<FormController*> controller,
|
not_null<FormController*> controller,
|
||||||
int fieldIndex,
|
int valueIndex,
|
||||||
const IdentityData &data,
|
const IdentityData &data,
|
||||||
std::vector<ScanInfo> &&files);
|
std::vector<ScanInfo> &&files);
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ private:
|
||||||
void save();
|
void save();
|
||||||
|
|
||||||
not_null<FormController*> _controller;
|
not_null<FormController*> _controller;
|
||||||
int _fieldIndex = -1;
|
int _valueIndex = -1;
|
||||||
|
|
||||||
std::vector<ScanInfo> _files;
|
std::vector<ScanInfo> _files;
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,8 @@ struct AesParams {
|
||||||
bytes::vector iv;
|
bytes::vector iv;
|
||||||
};
|
};
|
||||||
|
|
||||||
AesParams PrepareAesParams(bytes::const_span secretHash) {
|
AesParams PrepareAesParams(bytes::const_span bytesForEncryptionKey) {
|
||||||
const auto hash = openssl::Sha512(secretHash);
|
const auto hash = openssl::Sha512(bytesForEncryptionKey);
|
||||||
const auto view = gsl::make_span(hash);
|
const auto view = gsl::make_span(hash);
|
||||||
|
|
||||||
auto result = AesParams();
|
auto result = AesParams();
|
||||||
|
@ -82,14 +82,6 @@ bytes::vector Decrypt(
|
||||||
return EncryptOrDecrypt(encrypted, std::move(params), AES_DECRYPT);
|
return EncryptOrDecrypt(encrypted, std::move(params), AES_DECRYPT);
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes::vector PasswordHashForSecret(
|
|
||||||
bytes::const_span passwordUtf8) {
|
|
||||||
//new_secure_salt = new_salt + random_bytes(8) // #TODO
|
|
||||||
//password_hash = SHA512(new_secure_salt + password + new_secure_salt)
|
|
||||||
const auto result = openssl::Sha512(passwordUtf8);
|
|
||||||
return { result.begin(), result.end() };
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CheckBytesMod255(bytes::const_span bytes) {
|
bool CheckBytesMod255(bytes::const_span bytes) {
|
||||||
const auto full = ranges::accumulate(
|
const auto full = ranges::accumulate(
|
||||||
bytes,
|
bytes,
|
||||||
|
@ -119,7 +111,7 @@ bytes::vector GenerateSecretBytes() {
|
||||||
|
|
||||||
bytes::vector DecryptSecretBytes(
|
bytes::vector DecryptSecretBytes(
|
||||||
bytes::const_span encryptedSecret,
|
bytes::const_span encryptedSecret,
|
||||||
bytes::const_span passwordHashForSecret) {
|
bytes::const_span bytesForEncryptionKey) {
|
||||||
if (encryptedSecret.empty()) {
|
if (encryptedSecret.empty()) {
|
||||||
return {};
|
return {};
|
||||||
} else if (encryptedSecret.size() != kSecretSize) {
|
} else if (encryptedSecret.size() != kSecretSize) {
|
||||||
|
@ -127,7 +119,7 @@ bytes::vector DecryptSecretBytes(
|
||||||
).arg(encryptedSecret.size()));
|
).arg(encryptedSecret.size()));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
auto params = PrepareAesParams(passwordHashForSecret);
|
auto params = PrepareAesParams(bytesForEncryptionKey);
|
||||||
auto result = Decrypt(encryptedSecret, std::move(params));
|
auto result = Decrypt(encryptedSecret, std::move(params));
|
||||||
if (!CheckSecretBytes(result)) {
|
if (!CheckSecretBytes(result)) {
|
||||||
LOG(("API Error: Bad secret bytes."));
|
LOG(("API Error: Bad secret bytes."));
|
||||||
|
@ -138,11 +130,11 @@ bytes::vector DecryptSecretBytes(
|
||||||
|
|
||||||
bytes::vector EncryptSecretBytes(
|
bytes::vector EncryptSecretBytes(
|
||||||
bytes::const_span secret,
|
bytes::const_span secret,
|
||||||
bytes::const_span passwordHashForSecret) {
|
bytes::const_span bytesForEncryptionKey) {
|
||||||
Expects(secret.size() == kSecretSize);
|
Expects(secret.size() == kSecretSize);
|
||||||
Expects(CheckSecretBytes(secret) == true);
|
Expects(CheckSecretBytes(secret) == true);
|
||||||
|
|
||||||
auto params = PrepareAesParams(passwordHashForSecret);
|
auto params = PrepareAesParams(bytesForEncryptionKey);
|
||||||
return Encrypt(secret, std::move(params));
|
return Encrypt(secret, std::move(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,11 +146,26 @@ bytes::vector DecryptSecureSecret(
|
||||||
Expects(!encryptedSecret.empty());
|
Expects(!encryptedSecret.empty());
|
||||||
Expects(!password.empty());
|
Expects(!password.empty());
|
||||||
|
|
||||||
const auto hash = openssl::Sha512(bytes::concatenate(
|
const auto bytesForEncryptionKey = bytes::concatenate(
|
||||||
salt,
|
salt,
|
||||||
password,
|
password,
|
||||||
salt));
|
salt);
|
||||||
return DecryptSecretBytes(encryptedSecret, hash);
|
return DecryptSecretBytes(encryptedSecret, bytesForEncryptionKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes::vector EncryptSecureSecret(
|
||||||
|
bytes::const_span salt,
|
||||||
|
bytes::const_span secret,
|
||||||
|
bytes::const_span password) {
|
||||||
|
Expects(!salt.empty());
|
||||||
|
Expects(secret.size() == kSecretSize);
|
||||||
|
Expects(!password.empty());
|
||||||
|
|
||||||
|
const auto bytesForEncryptionKey = bytes::concatenate(
|
||||||
|
salt,
|
||||||
|
password,
|
||||||
|
salt);
|
||||||
|
return EncryptSecretBytes(secret, bytesForEncryptionKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes::vector SerializeData(const std::map<QString, QString> &data) {
|
bytes::vector SerializeData(const std::map<QString, QString> &data) {
|
||||||
|
@ -252,11 +259,11 @@ EncryptedData EncryptData(
|
||||||
gsl::make_span(unencrypted).subspan(padding),
|
gsl::make_span(unencrypted).subspan(padding),
|
||||||
bytes);
|
bytes);
|
||||||
const auto dataHash = openssl::Sha256(unencrypted);
|
const auto dataHash = openssl::Sha256(unencrypted);
|
||||||
const auto dataSecretHash = openssl::Sha512(bytes::concatenate(
|
const auto bytesForEncryptionKey = bytes::concatenate(
|
||||||
dataSecret,
|
dataSecret,
|
||||||
dataHash));
|
dataHash);
|
||||||
|
|
||||||
auto params = PrepareAesParams(dataSecretHash);
|
auto params = PrepareAesParams(bytesForEncryptionKey);
|
||||||
return {
|
return {
|
||||||
{ dataSecret.begin(), dataSecret.end() },
|
{ dataSecret.begin(), dataSecret.end() },
|
||||||
{ dataHash.begin(), dataHash.end() },
|
{ dataHash.begin(), dataHash.end() },
|
||||||
|
@ -279,10 +286,10 @@ bytes::vector DecryptData(
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto dataSecretHash = openssl::Sha512(bytes::concatenate(
|
const auto bytesForEncryptionKey = bytes::concatenate(
|
||||||
dataSecret,
|
dataSecret,
|
||||||
dataHash));
|
dataHash);
|
||||||
auto params = PrepareAesParams(dataSecretHash);
|
auto params = PrepareAesParams(bytesForEncryptionKey);
|
||||||
const auto decrypted = Decrypt(encrypted, std::move(params));
|
const auto decrypted = Decrypt(encrypted, std::move(params));
|
||||||
if (bytes::compare(openssl::Sha256(decrypted), dataHash) != 0) {
|
if (bytes::compare(openssl::Sha256(decrypted), dataHash) != 0) {
|
||||||
LOG(("API Error: Bad data hash."));
|
LOG(("API Error: Bad data hash."));
|
||||||
|
@ -317,19 +324,20 @@ bytes::vector EncryptValueSecret(
|
||||||
bytes::const_span valueSecret,
|
bytes::const_span valueSecret,
|
||||||
bytes::const_span secret,
|
bytes::const_span secret,
|
||||||
bytes::const_span valueHash) {
|
bytes::const_span valueHash) {
|
||||||
const auto valueSecretHash = openssl::Sha512(
|
const auto bytesForEncryptionKey = bytes::concatenate(
|
||||||
bytes::concatenate(secret, valueHash));
|
secret,
|
||||||
return EncryptSecretBytes(valueSecret, valueSecretHash);
|
valueHash);
|
||||||
|
return EncryptSecretBytes(valueSecret, bytesForEncryptionKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes::vector DecryptValueSecret(
|
bytes::vector DecryptValueSecret(
|
||||||
bytes::const_span encrypted,
|
bytes::const_span encrypted,
|
||||||
bytes::const_span secret,
|
bytes::const_span secret,
|
||||||
bytes::const_span valueHash) {
|
bytes::const_span valueHash) {
|
||||||
const auto valueSecretHash = openssl::Sha512(bytes::concatenate(
|
const auto bytesForEncryptionKey = bytes::concatenate(
|
||||||
secret,
|
secret,
|
||||||
valueHash));
|
valueHash);
|
||||||
return DecryptSecretBytes(encrypted, valueSecretHash);
|
return DecryptSecretBytes(encrypted, bytesForEncryptionKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64 CountSecureSecretHash(bytes::const_span secret) {
|
uint64 CountSecureSecretHash(bytes::const_span secret) {
|
||||||
|
|
|
@ -11,20 +11,15 @@ namespace Passport {
|
||||||
|
|
||||||
bytes::vector GenerateSecretBytes();
|
bytes::vector GenerateSecretBytes();
|
||||||
|
|
||||||
bytes::vector EncryptSecretBytes(
|
bytes::vector EncryptSecureSecret(
|
||||||
|
bytes::const_span salt,
|
||||||
bytes::const_span secret,
|
bytes::const_span secret,
|
||||||
bytes::const_span passwordHashForSecret);
|
bytes::const_span password);
|
||||||
bytes::vector DecryptSecretBytes(
|
|
||||||
bytes::const_span encryptedSecret,
|
|
||||||
bytes::const_span passwordHashForSecret);
|
|
||||||
|
|
||||||
bytes::vector DecryptSecureSecret(
|
bytes::vector DecryptSecureSecret(
|
||||||
bytes::const_span salt,
|
bytes::const_span salt,
|
||||||
bytes::const_span encryptedSecret,
|
bytes::const_span encryptedSecret,
|
||||||
bytes::const_span password);
|
bytes::const_span password);
|
||||||
|
|
||||||
bytes::vector PasswordHashForSecret(bytes::const_span passwordUtf8);
|
|
||||||
|
|
||||||
bytes::vector SerializeData(const std::map<QString, QString> &data);
|
bytes::vector SerializeData(const std::map<QString, QString> &data);
|
||||||
std::map<QString, QString> DeserializeData(bytes::const_span bytes);
|
std::map<QString, QString> DeserializeData(bytes::const_span bytes);
|
||||||
|
|
||||||
|
|
|
@ -172,7 +172,7 @@ void FormBox::Inner::refresh() {
|
||||||
if (_rows.size() <= index) {
|
if (_rows.size() <= index) {
|
||||||
_rows.push_back(object_ptr<FormRow>(this, title, description));
|
_rows.push_back(object_ptr<FormRow>(this, title, description));
|
||||||
_rows[index]->addClickHandler([=] {
|
_rows[index]->addClickHandler([=] {
|
||||||
_controller->editField(index);
|
_controller->editValue(index);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
_rows[index++]->setReady(ready);
|
_rows[index++]->setReady(ready);
|
||||||
|
|
|
@ -42,7 +42,7 @@ FormRequest::FormRequest(
|
||||||
, publicKey(publicKey) {
|
, publicKey(publicKey) {
|
||||||
}
|
}
|
||||||
|
|
||||||
FormController::UploadedScan::~UploadedScan() {
|
FormController::UploadScanData::~UploadScanData() {
|
||||||
if (fullId) {
|
if (fullId) {
|
||||||
Auth().uploader().cancel(fullId);
|
Auth().uploader().cancel(fullId);
|
||||||
}
|
}
|
||||||
|
@ -50,24 +50,12 @@ FormController::UploadedScan::~UploadedScan() {
|
||||||
|
|
||||||
FormController::EditFile::EditFile(
|
FormController::EditFile::EditFile(
|
||||||
const File &fields,
|
const File &fields,
|
||||||
std::unique_ptr<UploadedScan> &&uploaded)
|
std::unique_ptr<UploadScanData> &&uploadData)
|
||||||
: fields(std::move(fields))
|
: fields(std::move(fields))
|
||||||
, uploaded(std::move(uploaded)) {
|
, uploadData(std::move(uploadData)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
FormController::Field::Field(Type type) : type(type) {
|
FormController::Value::Value(Type type) : type(type) {
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FileHashes>
|
|
||||||
bytes::vector FormController::computeFilesHash(
|
|
||||||
FileHashes fileHashes,
|
|
||||||
bytes::const_span valueSecret) {
|
|
||||||
auto vec = bytes::concatenate(fileHashes);
|
|
||||||
auto hashesVector = std::vector<bytes::const_span>();
|
|
||||||
for (const auto &hash : fileHashes) {
|
|
||||||
hashesVector.push_back(bytes::make_span(hash));
|
|
||||||
}
|
|
||||||
return PrepareFilesHash(hashesVector, valueSecret);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FormController::FormController(
|
FormController::FormController(
|
||||||
|
@ -131,15 +119,17 @@ void FormController::validateSecureSecret(
|
||||||
if (!salt.empty() && !encryptedSecret.empty()) {
|
if (!salt.empty() && !encryptedSecret.empty()) {
|
||||||
_secret = DecryptSecureSecret(salt, encryptedSecret, password);
|
_secret = DecryptSecureSecret(salt, encryptedSecret, password);
|
||||||
if (_secret.empty()) {
|
if (_secret.empty()) {
|
||||||
|
_secretId = 0;
|
||||||
LOG(("API Error: Failed to decrypt secure secret. "
|
LOG(("API Error: Failed to decrypt secure secret. "
|
||||||
"Forgetting all files and data :("));
|
"Forgetting all files and data :("));
|
||||||
for (auto &field : _form.fields) {
|
for (auto &value : _form.rows) {
|
||||||
if (!field.encryptedSecret.empty()) {
|
if (!value.data.original.isEmpty()) {
|
||||||
resetField(field);
|
resetValue(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
decryptFields();
|
_secretId = CountSecureSecretHash(_secret);
|
||||||
|
decryptValues();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_secret.empty()) {
|
if (_secret.empty()) {
|
||||||
|
@ -148,61 +138,71 @@ void FormController::validateSecureSecret(
|
||||||
_secretReady.fire({});
|
_secretReady.fire({});
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::decryptFields() {
|
void FormController::decryptValues() {
|
||||||
Expects(!_secret.empty());
|
Expects(!_secret.empty());
|
||||||
|
|
||||||
for (auto &field : _form.fields) {
|
for (auto &value : _form.rows) {
|
||||||
if (field.encryptedSecret.empty()) {
|
if (value.data.original.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
decryptField(field);
|
decryptValue(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::decryptField(Field &field) {
|
void FormController::decryptValue(Value &value) {
|
||||||
Expects(!_secret.empty());
|
Expects(!_secret.empty());
|
||||||
Expects(!field.encryptedSecret.empty());
|
Expects(!value.data.original.isEmpty());
|
||||||
|
|
||||||
if (!validateFieldSecret(field)) {
|
if (!validateValueSecrets(value)) {
|
||||||
resetField(field);
|
resetValue(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto dataHash = bytes::make_span(field.dataHash);
|
value.data.parsed = DeserializeData(DecryptData(
|
||||||
field.parsedData = DeserializeData(DecryptData(
|
bytes::make_span(value.data.original),
|
||||||
bytes::make_span(field.originalData),
|
value.data.hash,
|
||||||
field.dataHash,
|
value.data.secret));
|
||||||
field.secret));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FormController::validateFieldSecret(Field &field) {
|
bool FormController::validateValueSecrets(Value &value) {
|
||||||
field.secret = DecryptValueSecret(
|
value.data.secret = DecryptValueSecret(
|
||||||
field.encryptedSecret,
|
value.data.encryptedSecret,
|
||||||
_secret,
|
_secret,
|
||||||
field.hash);
|
value.data.hash);
|
||||||
if (field.secret.empty()) {
|
if (value.data.secret.empty()) {
|
||||||
LOG(("API Error: Could not decrypt value secret. "
|
LOG(("API Error: Could not decrypt data secret. "
|
||||||
"Forgetting files and data :("));
|
"Forgetting files and data :("));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto fileHashes = ranges::view::all(
|
for (auto &file : value.files) {
|
||||||
field.files
|
file.secret = DecryptValueSecret(
|
||||||
|
file.encryptedSecret,
|
||||||
|
_secret,
|
||||||
|
file.hash);
|
||||||
|
if (file.secret.empty()) {
|
||||||
|
LOG(("API Error: Could not decrypt file secret. "
|
||||||
|
"Forgetting files and data :("));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto fileHashesSecrets = ranges::view::all(
|
||||||
|
value.files
|
||||||
) | ranges::view::transform([](File &file) {
|
) | ranges::view::transform([](File &file) {
|
||||||
return bytes::make_span(file.fileHash);
|
return bytes::concatenate(file.hash, file.encryptedSecret);
|
||||||
});
|
});
|
||||||
const auto countedHash = openssl::Sha256(bytes::concatenate(
|
const auto countedHash = openssl::Sha256(bytes::concatenate(
|
||||||
field.dataHash,
|
value.data.hash,
|
||||||
bytes::concatenate(fileHashes),
|
value.data.encryptedSecret,
|
||||||
field.secret));
|
bytes::concatenate(fileHashesSecrets)));
|
||||||
if (field.hash != countedHash) {
|
if (value.consistencyHash != countedHash) {
|
||||||
LOG(("API Error: Wrong hash after decrypting value secret. "
|
LOG(("API Error: Wrong hash after decrypting value secrets. "
|
||||||
"Forgetting files and data :("));
|
"Forgetting files and data :("));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::resetField(Field &field) {
|
void FormController::resetValue(Value &value) {
|
||||||
field = Field(field.type);
|
value = Value(value.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<QString> FormController::passwordError() const {
|
rpl::producer<QString> FormController::passwordError() const {
|
||||||
|
@ -213,52 +213,52 @@ QString FormController::passwordHint() const {
|
||||||
return _password.hint;
|
return _password.hint;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::uploadScan(int fieldIndex, QByteArray &&content) {
|
void FormController::uploadScan(int valueIndex, QByteArray &&content) {
|
||||||
Expects(_editBox != nullptr);
|
Expects(_editBox != nullptr);
|
||||||
Expects(fieldIndex >= 0 && fieldIndex < _form.fields.size());
|
Expects(valueIndex >= 0 && valueIndex < _form.rows.size());
|
||||||
|
|
||||||
auto &field = _form.fields[fieldIndex];
|
auto &value = _form.rows[valueIndex];
|
||||||
if (field.secret.empty()) {
|
auto fileIndex = int(value.filesInEdit.size());
|
||||||
field.secret = GenerateSecretBytes();
|
value.filesInEdit.emplace_back(
|
||||||
}
|
|
||||||
auto fileIndex = int(field.filesInEdit.size());
|
|
||||||
field.filesInEdit.emplace_back(
|
|
||||||
File(),
|
File(),
|
||||||
nullptr);
|
nullptr);
|
||||||
const auto fileId = rand_value<uint64>();
|
const auto fileId = rand_value<uint64>();
|
||||||
auto &file = field.filesInEdit.back();
|
auto &file = value.filesInEdit.back();
|
||||||
file.fields.size = content.size();
|
file.fields.size = content.size();
|
||||||
file.fields.id = fileId;
|
file.fields.id = fileId;
|
||||||
file.fields.dcId = MTP::maindc();
|
file.fields.dcId = MTP::maindc();
|
||||||
|
file.fields.secret = GenerateSecretBytes();
|
||||||
|
file.fields.date = unixtime();
|
||||||
file.fields.image = ReadImage(bytes::make_span(content));
|
file.fields.image = ReadImage(bytes::make_span(content));
|
||||||
file.fields.downloadOffset = file.fields.size;
|
file.fields.downloadOffset = file.fields.size;
|
||||||
|
|
||||||
_scanUpdated.fire(collectScanInfo(file));
|
_scanUpdated.fire(collectScanInfo(file));
|
||||||
|
|
||||||
encryptScan(fieldIndex, fileIndex, std::move(content));
|
encryptScan(valueIndex, fileIndex, std::move(content));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::encryptScan(
|
void FormController::encryptScan(
|
||||||
int fieldIndex,
|
int valueIndex,
|
||||||
int fileIndex,
|
int fileIndex,
|
||||||
QByteArray &&content) {
|
QByteArray &&content) {
|
||||||
Expects(_editBox != nullptr);
|
Expects(_editBox != nullptr);
|
||||||
Expects(fieldIndex >= 0 && fieldIndex < _form.fields.size());
|
Expects(valueIndex >= 0 && valueIndex < _form.rows.size());
|
||||||
Expects(fileIndex >= 0
|
Expects(fileIndex >= 0
|
||||||
&& fileIndex < _form.fields[fieldIndex].filesInEdit.size());
|
&& fileIndex < _form.rows[valueIndex].filesInEdit.size());
|
||||||
|
|
||||||
auto &field = _form.fields[fieldIndex];
|
const auto &value = _form.rows[valueIndex];
|
||||||
|
const auto &file = value.filesInEdit[fileIndex].fields;
|
||||||
const auto weak = _editBox;
|
const auto weak = _editBox;
|
||||||
crl::async([
|
crl::async([
|
||||||
=,
|
=,
|
||||||
fileId = field.filesInEdit[fileIndex].fields.id,
|
fileId = file.id,
|
||||||
bytes = std::move(content),
|
bytes = std::move(content),
|
||||||
valueSecret = field.secret
|
fileSecret = file.secret
|
||||||
] {
|
] {
|
||||||
auto data = EncryptData(
|
auto data = EncryptData(
|
||||||
bytes::make_span(bytes),
|
bytes::make_span(bytes),
|
||||||
valueSecret);
|
fileSecret);
|
||||||
auto result = UploadedScan();
|
auto result = UploadScanData();
|
||||||
result.fileId = fileId;
|
result.fileId = fileId;
|
||||||
result.hash = std::move(data.hash);
|
result.hash = std::move(data.hash);
|
||||||
result.bytes = std::move(data.bytes);
|
result.bytes = std::move(data.bytes);
|
||||||
|
@ -270,7 +270,7 @@ void FormController::encryptScan(
|
||||||
crl::on_main([=, encrypted = std::move(result)]() mutable {
|
crl::on_main([=, encrypted = std::move(result)]() mutable {
|
||||||
if (weak) {
|
if (weak) {
|
||||||
uploadEncryptedScan(
|
uploadEncryptedScan(
|
||||||
fieldIndex,
|
valueIndex,
|
||||||
fileIndex,
|
fileIndex,
|
||||||
std::move(encrypted));
|
std::move(encrypted));
|
||||||
}
|
}
|
||||||
|
@ -279,13 +279,13 @@ void FormController::encryptScan(
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::deleteScan(
|
void FormController::deleteScan(
|
||||||
int fieldIndex,
|
int valueIndex,
|
||||||
int fileIndex) {
|
int fileIndex) {
|
||||||
Expects(fieldIndex >= 0 && fieldIndex < _form.fields.size());
|
Expects(valueIndex >= 0 && valueIndex < _form.rows.size());
|
||||||
Expects(fileIndex >= 0
|
Expects(fileIndex >= 0
|
||||||
&& fileIndex < _form.fields[fieldIndex].filesInEdit.size());
|
&& fileIndex < _form.rows[valueIndex].filesInEdit.size());
|
||||||
|
|
||||||
auto &file = _form.fields[fieldIndex].filesInEdit[fileIndex];
|
auto &file = _form.rows[valueIndex].filesInEdit[fileIndex];
|
||||||
file.deleted = !file.deleted;
|
file.deleted = !file.deleted;
|
||||||
_scanUpdated.fire(collectScanInfo(file));
|
_scanUpdated.fire(collectScanInfo(file));
|
||||||
}
|
}
|
||||||
|
@ -296,14 +296,17 @@ void FormController::subscribeToUploader() {
|
||||||
}
|
}
|
||||||
|
|
||||||
using namespace Storage;
|
using namespace Storage;
|
||||||
|
|
||||||
Auth().uploader().secureReady(
|
Auth().uploader().secureReady(
|
||||||
) | rpl::start_with_next([=](const UploadSecureDone &data) {
|
) | rpl::start_with_next([=](const UploadSecureDone &data) {
|
||||||
scanUploadDone(data);
|
scanUploadDone(data);
|
||||||
}, _uploaderSubscriptions);
|
}, _uploaderSubscriptions);
|
||||||
|
|
||||||
Auth().uploader().secureProgress(
|
Auth().uploader().secureProgress(
|
||||||
) | rpl::start_with_next([=](const UploadSecureProgress &data) {
|
) | rpl::start_with_next([=](const UploadSecureProgress &data) {
|
||||||
scanUploadProgress(data);
|
scanUploadProgress(data);
|
||||||
}, _uploaderSubscriptions);
|
}, _uploaderSubscriptions);
|
||||||
|
|
||||||
Auth().uploader().secureFailed(
|
Auth().uploader().secureFailed(
|
||||||
) | rpl::start_with_next([=](const FullMsgId &fullId) {
|
) | rpl::start_with_next([=](const FullMsgId &fullId) {
|
||||||
scanUploadFail(fullId);
|
scanUploadFail(fullId);
|
||||||
|
@ -311,44 +314,48 @@ void FormController::subscribeToUploader() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::uploadEncryptedScan(
|
void FormController::uploadEncryptedScan(
|
||||||
int fieldIndex,
|
int valueIndex,
|
||||||
int fileIndex,
|
int fileIndex,
|
||||||
UploadedScan &&data) {
|
UploadScanData &&data) {
|
||||||
Expects(_editBox != nullptr);
|
Expects(_editBox != nullptr);
|
||||||
Expects(fieldIndex >= 0 && fieldIndex < _form.fields.size());
|
Expects(valueIndex >= 0 && valueIndex < _form.rows.size());
|
||||||
Expects(fileIndex >= 0
|
Expects(fileIndex >= 0
|
||||||
&& fileIndex < _form.fields[fieldIndex].filesInEdit.size());
|
&& fileIndex < _form.rows[valueIndex].filesInEdit.size());
|
||||||
|
|
||||||
subscribeToUploader();
|
subscribeToUploader();
|
||||||
|
|
||||||
auto &file = _form.fields[fieldIndex].filesInEdit[fileIndex];
|
auto &file = _form.rows[valueIndex].filesInEdit[fileIndex];
|
||||||
file.uploaded = std::make_unique<UploadedScan>(std::move(data));
|
file.uploadData = std::make_unique<UploadScanData>(std::move(data));
|
||||||
|
|
||||||
auto prepared = std::make_shared<FileLoadResult>(
|
auto prepared = std::make_shared<FileLoadResult>(
|
||||||
TaskId(),
|
TaskId(),
|
||||||
file.uploaded->fileId,
|
file.uploadData->fileId,
|
||||||
FileLoadTo(PeerId(0), false, MsgId(0)),
|
FileLoadTo(PeerId(0), false, MsgId(0)),
|
||||||
TextWithTags(),
|
TextWithTags(),
|
||||||
std::shared_ptr<SendingAlbum>(nullptr));
|
std::shared_ptr<SendingAlbum>(nullptr));
|
||||||
prepared->type = SendMediaType::Secure;
|
prepared->type = SendMediaType::Secure;
|
||||||
prepared->content = QByteArray::fromRawData(
|
prepared->content = QByteArray::fromRawData(
|
||||||
reinterpret_cast<char*>(file.uploaded->bytes.data()),
|
reinterpret_cast<char*>(file.uploadData->bytes.data()),
|
||||||
file.uploaded->bytes.size());
|
file.uploadData->bytes.size());
|
||||||
prepared->setFileData(prepared->content);
|
prepared->setFileData(prepared->content);
|
||||||
prepared->filemd5 = file.uploaded->md5checksum;
|
prepared->filemd5 = file.uploadData->md5checksum;
|
||||||
|
|
||||||
file.uploaded->fullId = FullMsgId(0, clientMsgId());
|
file.uploadData->fullId = FullMsgId(0, clientMsgId());
|
||||||
Auth().uploader().upload(file.uploaded->fullId, std::move(prepared));
|
Auth().uploader().upload(file.uploadData->fullId, std::move(prepared));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::scanUploadDone(const Storage::UploadSecureDone &data) {
|
void FormController::scanUploadDone(const Storage::UploadSecureDone &data) {
|
||||||
if (const auto file = findEditFile(data.fullId)) {
|
if (const auto file = findEditFile(data.fullId)) {
|
||||||
Assert(file->uploaded != nullptr);
|
Assert(file->uploadData != nullptr);
|
||||||
Assert(file->uploaded->fileId == data.fileId);
|
Assert(file->uploadData->fileId == data.fileId);
|
||||||
|
|
||||||
file->uploaded->partsCount = data.partsCount;
|
file->uploadData->partsCount = data.partsCount;
|
||||||
file->fields.fileHash = std::move(file->uploaded->hash);
|
file->fields.hash = std::move(file->uploadData->hash);
|
||||||
file->uploaded->fullId = FullMsgId();
|
file->fields.encryptedSecret = EncryptValueSecret(
|
||||||
|
file->fields.secret,
|
||||||
|
_secret,
|
||||||
|
file->fields.hash);
|
||||||
|
file->uploadData->fullId = FullMsgId();
|
||||||
|
|
||||||
_scanUpdated.fire(collectScanInfo(*file));
|
_scanUpdated.fire(collectScanInfo(*file));
|
||||||
}
|
}
|
||||||
|
@ -357,9 +364,9 @@ void FormController::scanUploadDone(const Storage::UploadSecureDone &data) {
|
||||||
void FormController::scanUploadProgress(
|
void FormController::scanUploadProgress(
|
||||||
const Storage::UploadSecureProgress &data) {
|
const Storage::UploadSecureProgress &data) {
|
||||||
if (const auto file = findEditFile(data.fullId)) {
|
if (const auto file = findEditFile(data.fullId)) {
|
||||||
Assert(file->uploaded != nullptr);
|
Assert(file->uploadData != nullptr);
|
||||||
|
|
||||||
file->uploaded->offset = data.offset;
|
file->uploadData->offset = data.offset;
|
||||||
|
|
||||||
_scanUpdated.fire(collectScanInfo(*file));
|
_scanUpdated.fire(collectScanInfo(*file));
|
||||||
}
|
}
|
||||||
|
@ -367,9 +374,9 @@ void FormController::scanUploadProgress(
|
||||||
|
|
||||||
void FormController::scanUploadFail(const FullMsgId &fullId) {
|
void FormController::scanUploadFail(const FullMsgId &fullId) {
|
||||||
if (const auto file = findEditFile(fullId)) {
|
if (const auto file = findEditFile(fullId)) {
|
||||||
Assert(file->uploaded != nullptr);
|
Assert(file->uploadData != nullptr);
|
||||||
|
|
||||||
file->uploaded->offset = -1;
|
file->uploadData->offset = -1;
|
||||||
|
|
||||||
_scanUpdated.fire(collectScanInfo(*file));
|
_scanUpdated.fire(collectScanInfo(*file));
|
||||||
}
|
}
|
||||||
|
@ -399,27 +406,27 @@ void FormController::fillRows(
|
||||||
QString title,
|
QString title,
|
||||||
QString description,
|
QString description,
|
||||||
bool ready)> callback) {
|
bool ready)> callback) {
|
||||||
for (const auto &field : _form.fields) {
|
for (const auto &value : _form.rows) {
|
||||||
switch (field.type) {
|
switch (value.type) {
|
||||||
case Field::Type::Identity:
|
case Value::Type::Identity:
|
||||||
callback(
|
callback(
|
||||||
lang(lng_passport_identity_title),
|
lang(lng_passport_identity_title),
|
||||||
lang(lng_passport_identity_description),
|
lang(lng_passport_identity_description),
|
||||||
false);
|
false);
|
||||||
break;
|
break;
|
||||||
case Field::Type::Address:
|
case Value::Type::Address:
|
||||||
callback(
|
callback(
|
||||||
lang(lng_passport_address_title),
|
lang(lng_passport_address_title),
|
||||||
lang(lng_passport_address_description),
|
lang(lng_passport_address_description),
|
||||||
false);
|
false);
|
||||||
break;
|
break;
|
||||||
case Field::Type::Phone:
|
case Value::Type::Phone:
|
||||||
callback(
|
callback(
|
||||||
lang(lng_passport_phone_title),
|
lang(lng_passport_phone_title),
|
||||||
App::self()->phone(),
|
App::self()->phone(),
|
||||||
true);
|
true);
|
||||||
break;
|
break;
|
||||||
case Field::Type::Email:
|
case Value::Type::Email:
|
||||||
callback(
|
callback(
|
||||||
lang(lng_passport_email_title),
|
lang(lng_passport_email_title),
|
||||||
lang(lng_passport_email_description),
|
lang(lng_passport_email_description),
|
||||||
|
@ -429,24 +436,25 @@ void FormController::fillRows(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::editField(int index) {
|
void FormController::editValue(int index) {
|
||||||
Expects(index >= 0 && index < _form.fields.size());
|
Expects(index >= 0 && index < _form.rows.size());
|
||||||
|
|
||||||
|
auto &value = _form.rows[index];
|
||||||
|
loadFiles(value.files);
|
||||||
|
value.filesInEdit = ranges::view::all(
|
||||||
|
value.files
|
||||||
|
) | ranges::view::transform([](const File &file) {
|
||||||
|
return EditFile(file, nullptr);
|
||||||
|
}) | ranges::to_vector;
|
||||||
|
|
||||||
auto box = [&]() -> object_ptr<BoxContent> {
|
auto box = [&]() -> object_ptr<BoxContent> {
|
||||||
auto &field = _form.fields[index];
|
switch (value.type) {
|
||||||
switch (field.type) {
|
case Value::Type::Identity:
|
||||||
case Field::Type::Identity:
|
|
||||||
loadFiles(field.files);
|
|
||||||
field.filesInEdit = ranges::view::all(
|
|
||||||
field.files
|
|
||||||
) | ranges::view::transform([](const File &file) {
|
|
||||||
return EditFile(file, nullptr);
|
|
||||||
}) | ranges::to_vector;
|
|
||||||
return Box<IdentityBox>(
|
return Box<IdentityBox>(
|
||||||
this,
|
this,
|
||||||
index,
|
index,
|
||||||
fieldDataIdentity(field),
|
valueDataIdentity(value),
|
||||||
fieldFilesIdentity(field));
|
valueFilesIdentity(value));
|
||||||
}
|
}
|
||||||
return { nullptr };
|
return { nullptr };
|
||||||
}();
|
}();
|
||||||
|
@ -496,11 +504,15 @@ void FormController::loadFiles(std::vector<File> &files) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::fileLoadDone(FileKey key, const QByteArray &bytes) {
|
void FormController::fileLoadDone(FileKey key, const QByteArray &bytes) {
|
||||||
if (const auto [field, file] = findFile(key); file != nullptr) {
|
if (const auto [value, file] = findFile(key); file != nullptr) {
|
||||||
const auto decrypted = DecryptData(
|
const auto decrypted = DecryptData(
|
||||||
bytes::make_span(bytes),
|
bytes::make_span(bytes),
|
||||||
file->fileHash,
|
file->hash,
|
||||||
field->secret);
|
file->secret);
|
||||||
|
if (decrypted.empty()) {
|
||||||
|
fileLoadFail(key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
file->downloadOffset = file->size;
|
file->downloadOffset = file->size;
|
||||||
file->image = App::readImage(QByteArray::fromRawData(
|
file->image = App::readImage(QByteArray::fromRawData(
|
||||||
reinterpret_cast<const char*>(decrypted.data()),
|
reinterpret_cast<const char*>(decrypted.data()),
|
||||||
|
@ -514,7 +526,7 @@ void FormController::fileLoadDone(FileKey key, const QByteArray &bytes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::fileLoadProgress(FileKey key, int offset) {
|
void FormController::fileLoadProgress(FileKey key, int offset) {
|
||||||
if (const auto [field, file] = findFile(key); file != nullptr) {
|
if (const auto [value, file] = findFile(key); file != nullptr) {
|
||||||
file->downloadOffset = offset;
|
file->downloadOffset = offset;
|
||||||
if (const auto fileInEdit = findEditFile(key)) {
|
if (const auto fileInEdit = findEditFile(key)) {
|
||||||
fileInEdit->fields.downloadOffset = file->downloadOffset;
|
fileInEdit->fields.downloadOffset = file->downloadOffset;
|
||||||
|
@ -524,7 +536,7 @@ void FormController::fileLoadProgress(FileKey key, int offset) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::fileLoadFail(FileKey key) {
|
void FormController::fileLoadFail(FileKey key) {
|
||||||
if (const auto [field, file] = findFile(key); file != nullptr) {
|
if (const auto [value, file] = findFile(key); file != nullptr) {
|
||||||
file->downloadOffset = -1;
|
file->downloadOffset = -1;
|
||||||
if (const auto fileInEdit = findEditFile(key)) {
|
if (const auto fileInEdit = findEditFile(key)) {
|
||||||
fileInEdit->fields.downloadOffset = file->downloadOffset;
|
fileInEdit->fields.downloadOffset = file->downloadOffset;
|
||||||
|
@ -545,15 +557,16 @@ ScanInfo FormController::collectScanInfo(const EditFile &file) const {
|
||||||
).arg(file.fields.downloadOffset
|
).arg(file.fields.downloadOffset
|
||||||
).arg(file.fields.size);
|
).arg(file.fields.size);
|
||||||
} else {
|
} else {
|
||||||
return QString("download ready");
|
return QString("uploaded ")
|
||||||
|
+ langDateTimeFull(ParseDateTime(file.fields.date));
|
||||||
}
|
}
|
||||||
} else if (file.uploaded) {
|
} else if (file.uploadData) {
|
||||||
if (file.uploaded->offset < 0) {
|
if (file.uploadData->offset < 0) {
|
||||||
return QString("upload failed");
|
return QString("upload failed");
|
||||||
} else if (file.uploaded->fullId) {
|
} else if (file.uploadData->fullId) {
|
||||||
return QString("uploading %1 / %2"
|
return QString("uploading %1 / %2"
|
||||||
).arg(file.uploaded->offset
|
).arg(file.uploadData->offset
|
||||||
).arg(file.uploaded->bytes.size());
|
).arg(file.uploadData->bytes.size());
|
||||||
} else {
|
} else {
|
||||||
return QString("upload ready");
|
return QString("upload ready");
|
||||||
}
|
}
|
||||||
|
@ -567,8 +580,8 @@ ScanInfo FormController::collectScanInfo(const EditFile &file) const {
|
||||||
file.fields.image };
|
file.fields.image };
|
||||||
}
|
}
|
||||||
|
|
||||||
IdentityData FormController::fieldDataIdentity(const Field &field) const {
|
IdentityData FormController::valueDataIdentity(const Value &value) const {
|
||||||
const auto &map = field.parsedData;
|
const auto &map = value.data.parsed;
|
||||||
auto result = IdentityData();
|
auto result = IdentityData();
|
||||||
if (const auto i = map.find(qsl("first_name")); i != map.cend()) {
|
if (const auto i = map.find(qsl("first_name")); i != map.cend()) {
|
||||||
result.name = i->second;
|
result.name = i->second;
|
||||||
|
@ -579,31 +592,31 @@ IdentityData FormController::fieldDataIdentity(const Field &field) const {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ScanInfo> FormController::fieldFilesIdentity(
|
std::vector<ScanInfo> FormController::valueFilesIdentity(
|
||||||
const Field &field) const {
|
const Value &value) const {
|
||||||
auto result = std::vector<ScanInfo>();
|
auto result = std::vector<ScanInfo>();
|
||||||
for (const auto &file : field.filesInEdit) {
|
for (const auto &file : value.filesInEdit) {
|
||||||
result.push_back(collectScanInfo(file));
|
result.push_back(collectScanInfo(file));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::saveFieldIdentity(
|
void FormController::saveValueIdentity(
|
||||||
int index,
|
int index,
|
||||||
const IdentityData &data) {
|
const IdentityData &data) {
|
||||||
Expects(_editBox != nullptr);
|
Expects(_editBox != nullptr);
|
||||||
Expects(index >= 0 && index < _form.fields.size());
|
Expects(index >= 0 && index < _form.rows.size());
|
||||||
Expects(_form.fields[index].type == Field::Type::Identity);
|
Expects(_form.rows[index].type == Value::Type::Identity);
|
||||||
|
|
||||||
_form.fields[index].parsedData[qsl("first_name")] = data.name;
|
_form.rows[index].data.parsed[qsl("first_name")] = data.name;
|
||||||
_form.fields[index].parsedData[qsl("last_name")] = data.surname;
|
_form.rows[index].data.parsed[qsl("last_name")] = data.surname;
|
||||||
|
|
||||||
saveIdentity(index);
|
saveIdentity(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::saveIdentity(int index) {
|
void FormController::saveIdentity(int index) {
|
||||||
Expects(index >= 0 && index < _form.fields.size());
|
Expects(index >= 0 && index < _form.rows.size());
|
||||||
Expects(_form.fields[index].type == Field::Type::Identity);
|
Expects(_form.rows[index].type == Value::Type::Identity);
|
||||||
|
|
||||||
if (_secret.empty()) {
|
if (_secret.empty()) {
|
||||||
_secretCallbacks.push_back([=] {
|
_secretCallbacks.push_back([=] {
|
||||||
|
@ -614,63 +627,69 @@ void FormController::saveIdentity(int index) {
|
||||||
|
|
||||||
_editBox->closeBox();
|
_editBox->closeBox();
|
||||||
|
|
||||||
auto &field = _form.fields[index];
|
auto &value = _form.rows[index];
|
||||||
|
|
||||||
auto inputFiles = QVector<MTPInputSecureFile>();
|
auto inputFiles = QVector<MTPInputSecureFile>();
|
||||||
inputFiles.reserve(field.filesInEdit.size());
|
inputFiles.reserve(value.filesInEdit.size());
|
||||||
for (const auto &file : field.filesInEdit) {
|
for (const auto &file : value.filesInEdit) {
|
||||||
if (file.deleted) {
|
if (file.deleted) {
|
||||||
continue;
|
continue;
|
||||||
} else if (const auto uploaded = file.uploaded.get()) {
|
} else if (const auto uploadData = file.uploadData.get()) {
|
||||||
inputFiles.push_back(MTP_inputSecureFileUploaded(
|
inputFiles.push_back(MTP_inputSecureFileUploaded(
|
||||||
MTP_long(file.fields.id),
|
MTP_long(file.fields.id),
|
||||||
MTP_int(uploaded->partsCount),
|
MTP_int(uploadData->partsCount),
|
||||||
MTP_bytes(uploaded->md5checksum),
|
MTP_bytes(uploadData->md5checksum),
|
||||||
MTP_bytes(file.fields.fileHash)));
|
MTP_bytes(file.fields.hash),
|
||||||
|
MTP_bytes(file.fields.encryptedSecret)));
|
||||||
} else {
|
} else {
|
||||||
inputFiles.push_back(MTP_inputSecureFile(
|
inputFiles.push_back(MTP_inputSecureFile(
|
||||||
MTP_long(file.fields.id),
|
MTP_long(file.fields.id),
|
||||||
MTP_long(file.fields.accessHash)));
|
MTP_long(file.fields.accessHash)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (field.secret.empty()) {
|
|
||||||
field.secret = GenerateSecretBytes();
|
if (value.data.secret.empty()) {
|
||||||
|
value.data.secret = GenerateSecretBytes();
|
||||||
}
|
}
|
||||||
const auto encryptedData = EncryptData(
|
const auto encryptedData = EncryptData(
|
||||||
SerializeData(field.parsedData),
|
SerializeData(value.data.parsed),
|
||||||
field.secret);
|
value.data.secret);
|
||||||
const auto fileHashes = ranges::view::all(
|
value.data.hash = encryptedData.hash;
|
||||||
field.filesInEdit
|
value.data.encryptedSecret = EncryptValueSecret(
|
||||||
|
value.data.secret,
|
||||||
|
_secret,
|
||||||
|
value.data.hash);
|
||||||
|
|
||||||
|
const auto fileHashesSecrets = ranges::view::all(
|
||||||
|
value.filesInEdit
|
||||||
) | ranges::view::filter([](const EditFile &file) {
|
) | ranges::view::filter([](const EditFile &file) {
|
||||||
return !file.deleted;
|
return !file.deleted;
|
||||||
}) | ranges::view::transform([](const EditFile &file) {
|
}) | ranges::view::transform([](const EditFile &file) {
|
||||||
return bytes::make_span(file.fields.fileHash);
|
return bytes::concatenate(
|
||||||
|
file.fields.hash,
|
||||||
|
file.fields.encryptedSecret);
|
||||||
});
|
});
|
||||||
const auto valueHash = openssl::Sha256(bytes::concatenate(
|
value.consistencyHash = openssl::Sha256(bytes::concatenate(
|
||||||
encryptedData.hash,
|
value.data.hash,
|
||||||
bytes::concatenate(fileHashes),
|
value.data.encryptedSecret,
|
||||||
field.secret));
|
bytes::concatenate(fileHashesSecrets)));
|
||||||
|
|
||||||
field.encryptedSecret = EncryptValueSecret(
|
|
||||||
field.secret,
|
|
||||||
_secret,
|
|
||||||
valueHash);
|
|
||||||
request(MTPaccount_SaveSecureValue(
|
request(MTPaccount_SaveSecureValue(
|
||||||
MTP_inputSecureValueIdentity(
|
MTP_inputSecureValueIdentity(
|
||||||
MTP_secureData(
|
MTP_secureData(
|
||||||
MTP_bytes(encryptedData.bytes),
|
MTP_bytes(encryptedData.bytes),
|
||||||
MTP_bytes(encryptedData.hash)),
|
MTP_bytes(value.data.hash),
|
||||||
|
MTP_bytes(value.data.encryptedSecret)),
|
||||||
MTP_vector<MTPInputSecureFile>(inputFiles),
|
MTP_vector<MTPInputSecureFile>(inputFiles),
|
||||||
MTP_bytes(field.encryptedSecret),
|
MTP_bytes(value.consistencyHash)),
|
||||||
MTP_bytes(valueHash)),
|
|
||||||
MTP_long(CountSecureSecretHash(_secret))
|
MTP_long(CountSecureSecretHash(_secret))
|
||||||
)).done([=](const MTPSecureValueSaved &result) {
|
)).done([=](const MTPSecureValueSaved &result) {
|
||||||
Expects(result.type() == mtpc_secureValueSaved);
|
Expects(result.type() == mtpc_secureValueSaved);
|
||||||
|
|
||||||
const auto &data = result.c_secureValueSaved();
|
const auto &data = result.c_secureValueSaved();
|
||||||
_form.fields[index].files = parseFiles(
|
_form.rows[index].files = parseFiles(
|
||||||
data.vfiles.v,
|
data.vfiles.v,
|
||||||
base::take(_form.fields[index].filesInEdit));
|
base::take(_form.rows[index].filesInEdit));
|
||||||
|
|
||||||
Ui::show(Box<InformBox>("Saved"), LayerOption::KeepOther);
|
Ui::show(Box<InformBox>("Saved"), LayerOption::KeepOther);
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
|
@ -690,19 +709,17 @@ void FormController::generateSecret(bytes::const_span password) {
|
||||||
_password.newSecureSalt,
|
_password.newSecureSalt,
|
||||||
randomSaltPart);
|
randomSaltPart);
|
||||||
|
|
||||||
const auto hashForSecret = openssl::Sha512(bytes::concatenate(
|
auto secureSecretId = CountSecureSecretHash(secret);
|
||||||
|
auto encryptedSecret = EncryptSecureSecret(
|
||||||
newSecureSaltFull,
|
newSecureSaltFull,
|
||||||
password,
|
secret,
|
||||||
newSecureSaltFull));
|
password);
|
||||||
|
|
||||||
const auto hashForAuth = openssl::Sha256(bytes::concatenate(
|
const auto hashForAuth = openssl::Sha256(bytes::concatenate(
|
||||||
_password.salt,
|
_password.salt,
|
||||||
password,
|
password,
|
||||||
_password.salt));
|
_password.salt));
|
||||||
|
|
||||||
auto secureSecretHash = CountSecureSecretHash(secret);
|
|
||||||
auto encryptedSecret = EncryptSecretBytes(
|
|
||||||
secret,
|
|
||||||
hashForSecret);
|
|
||||||
using Flag = MTPDaccount_passwordInputSettings::Flag;
|
using Flag = MTPDaccount_passwordInputSettings::Flag;
|
||||||
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
|
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
|
||||||
MTP_bytes(hashForAuth),
|
MTP_bytes(hashForAuth),
|
||||||
|
@ -714,10 +731,11 @@ void FormController::generateSecret(bytes::const_span password) {
|
||||||
MTPstring(), // email
|
MTPstring(), // email
|
||||||
MTP_bytes(newSecureSaltFull),
|
MTP_bytes(newSecureSaltFull),
|
||||||
MTP_bytes(encryptedSecret),
|
MTP_bytes(encryptedSecret),
|
||||||
MTP_long(secureSecretHash))
|
MTP_long(secureSecretId))
|
||||||
)).done([=](const MTPBool &result) {
|
)).done([=](const MTPBool &result) {
|
||||||
_saveSecretRequestId = 0;
|
_saveSecretRequestId = 0;
|
||||||
_secret = secret;
|
_secret = secret;
|
||||||
|
_secretId = secureSecretId;
|
||||||
//_password.salt = newPasswordSaltFull;
|
//_password.salt = newPasswordSaltFull;
|
||||||
for (const auto &callback : base::take(_secretCallbacks)) {
|
for (const auto &callback : base::take(_secretCallbacks)) {
|
||||||
callback();
|
callback();
|
||||||
|
@ -745,20 +763,20 @@ void FormController::requestForm() {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename DataType>
|
template <typename DataType>
|
||||||
auto FormController::parseEncryptedField(
|
auto FormController::parseEncryptedValue(
|
||||||
Field::Type type,
|
Value::Type type,
|
||||||
const DataType &data) const -> Field {
|
const DataType &data) const -> Value {
|
||||||
Expects(data.vdata.type() == mtpc_secureData);
|
Expects(data.vdata.type() == mtpc_secureData);
|
||||||
|
|
||||||
auto result = Field(type);
|
auto result = Value(type);
|
||||||
if (data.has_verified()) {
|
if (data.has_verified()) {
|
||||||
result.verification = parseVerified(data.vverified);
|
result.verification = parseVerified(data.vverified);
|
||||||
}
|
}
|
||||||
result.encryptedSecret = bytes::make_vector(data.vsecret.v);
|
result.consistencyHash = bytes::make_vector(data.vhash.v);
|
||||||
result.hash = bytes::make_vector(data.vhash.v);
|
|
||||||
const auto &fields = data.vdata.c_secureData();
|
const auto &fields = data.vdata.c_secureData();
|
||||||
result.originalData = fields.vdata.v;
|
result.data.original = fields.vdata.v;
|
||||||
result.dataHash = bytes::make_vector(fields.vdata_hash.v);
|
result.data.hash = bytes::make_vector(fields.vdata_hash.v);
|
||||||
|
result.data.encryptedSecret = bytes::make_vector(fields.vsecret.v);
|
||||||
result.files = parseFiles(data.vfiles.v);
|
result.files = parseFiles(data.vfiles.v);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -782,28 +800,11 @@ auto FormController::parseFiles(
|
||||||
normal.id = fields.vid.v;
|
normal.id = fields.vid.v;
|
||||||
normal.accessHash = fields.vaccess_hash.v;
|
normal.accessHash = fields.vaccess_hash.v;
|
||||||
normal.size = fields.vsize.v;
|
normal.size = fields.vsize.v;
|
||||||
|
normal.date = fields.vdate.v;
|
||||||
normal.dcId = fields.vdc_id.v;
|
normal.dcId = fields.vdc_id.v;
|
||||||
normal.fileHash = bytes::make_vector(fields.vfile_hash.v);
|
normal.hash = bytes::make_vector(fields.vfile_hash.v);
|
||||||
const auto i = ranges::find(
|
normal.encryptedSecret = bytes::make_vector(fields.vsecret.v);
|
||||||
editData,
|
fillDownloadedFile(normal, editData);
|
||||||
normal.fileHash,
|
|
||||||
[](const EditFile &file) { return file.fields.fileHash; });
|
|
||||||
if (i != editData.end()) {
|
|
||||||
normal.image = i->fields.image;
|
|
||||||
normal.downloadOffset = i->fields.downloadOffset;
|
|
||||||
if (i->uploaded) {
|
|
||||||
Local::writeImage(
|
|
||||||
StorageKey(
|
|
||||||
storageMix32To64(
|
|
||||||
SecureFileLocation,
|
|
||||||
normal.dcId),
|
|
||||||
normal.id),
|
|
||||||
StorageImageSaved(QByteArray::fromRawData(
|
|
||||||
reinterpret_cast<const char*>(
|
|
||||||
i->uploaded->bytes.data()),
|
|
||||||
i->uploaded->bytes.size())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result.push_back(std::move(normal));
|
result.push_back(std::move(normal));
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
@ -813,12 +814,39 @@ auto FormController::parseFiles(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FormController::fillDownloadedFile(
|
||||||
|
File &destination,
|
||||||
|
const std::vector<EditFile> &source) const {
|
||||||
|
const auto i = ranges::find(
|
||||||
|
source,
|
||||||
|
destination.hash,
|
||||||
|
[](const EditFile &file) { return file.fields.hash; });
|
||||||
|
if (i == source.end()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
destination.image = i->fields.image;
|
||||||
|
destination.downloadOffset = i->fields.downloadOffset;
|
||||||
|
if (!i->uploadData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Local::writeImage(
|
||||||
|
StorageKey(
|
||||||
|
storageMix32To64(
|
||||||
|
SecureFileLocation,
|
||||||
|
destination.dcId),
|
||||||
|
destination.id),
|
||||||
|
StorageImageSaved(QByteArray::fromRawData(
|
||||||
|
reinterpret_cast<const char*>(
|
||||||
|
i->uploadData->bytes.data()),
|
||||||
|
i->uploadData->bytes.size())));
|
||||||
|
}
|
||||||
|
|
||||||
template <typename DataType>
|
template <typename DataType>
|
||||||
auto FormController::parsePlainTextField(
|
auto FormController::parsePlainTextValue(
|
||||||
Field::Type type,
|
Value::Type type,
|
||||||
const QByteArray &value,
|
const QByteArray &value,
|
||||||
const DataType &data) const -> Field {
|
const DataType &data) const -> Value {
|
||||||
auto result = Field(type);
|
auto result = Value(type);
|
||||||
const auto check = bytes::compare(
|
const auto check = bytes::compare(
|
||||||
bytes::make_span(data.vhash.v),
|
bytes::make_span(data.vhash.v),
|
||||||
openssl::Sha256(bytes::make_span(value)));
|
openssl::Sha256(bytes::make_span(value)));
|
||||||
|
@ -830,38 +858,38 @@ auto FormController::parsePlainTextField(
|
||||||
));
|
));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
result.parsedData[QString("value")] = QString::fromUtf8(value);
|
result.data.parsed[QString("value")] = QString::fromUtf8(value);
|
||||||
if (data.has_verified()) {
|
if (data.has_verified()) {
|
||||||
result.verification = parseVerified(data.vverified);
|
result.verification = parseVerified(data.vverified);
|
||||||
}
|
}
|
||||||
result.hash = bytes::make_vector(data.vhash.v);
|
result.consistencyHash = bytes::make_vector(data.vhash.v);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto FormController::parseValue(
|
auto FormController::parseValue(
|
||||||
const MTPSecureValue &value) const -> Field {
|
const MTPSecureValue &value) const -> Value {
|
||||||
switch (value.type()) {
|
switch (value.type()) {
|
||||||
case mtpc_secureValueIdentity: {
|
case mtpc_secureValueIdentity: {
|
||||||
return parseEncryptedField(
|
return parseEncryptedValue(
|
||||||
Field::Type::Identity,
|
Value::Type::Identity,
|
||||||
value.c_secureValueIdentity());
|
value.c_secureValueIdentity());
|
||||||
} break;
|
} break;
|
||||||
case mtpc_secureValueAddress: {
|
case mtpc_secureValueAddress: {
|
||||||
return parseEncryptedField(
|
return parseEncryptedValue(
|
||||||
Field::Type::Address,
|
Value::Type::Address,
|
||||||
value.c_secureValueAddress());
|
value.c_secureValueAddress());
|
||||||
} break;
|
} break;
|
||||||
case mtpc_secureValuePhone: {
|
case mtpc_secureValuePhone: {
|
||||||
const auto &data = value.c_secureValuePhone();
|
const auto &data = value.c_secureValuePhone();
|
||||||
return parsePlainTextField(
|
return parsePlainTextValue(
|
||||||
Field::Type::Phone,
|
Value::Type::Phone,
|
||||||
data.vphone.v,
|
data.vphone.v,
|
||||||
data);
|
data);
|
||||||
} break;
|
} break;
|
||||||
case mtpc_secureValueEmail: {
|
case mtpc_secureValueEmail: {
|
||||||
const auto &data = value.c_secureValueEmail();
|
const auto &data = value.c_secureValueEmail();
|
||||||
return parsePlainTextField(
|
return parsePlainTextValue(
|
||||||
Field::Type::Phone,
|
Value::Type::Phone,
|
||||||
data.vemail.v,
|
data.vemail.v,
|
||||||
data);
|
data);
|
||||||
} break;
|
} break;
|
||||||
|
@ -874,13 +902,13 @@ auto FormController::parseVerified(const MTPSecureValueVerified &data) const
|
||||||
Expects(data.type() == mtpc_secureValueVerified);
|
Expects(data.type() == mtpc_secureValueVerified);
|
||||||
|
|
||||||
const auto &fields = data.c_secureValueVerified();
|
const auto &fields = data.c_secureValueVerified();
|
||||||
return Verification{ fields.vdate.v, qs(fields.vprovider) };
|
return Verification{ fields.vdate.v };
|
||||||
}
|
}
|
||||||
|
|
||||||
auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* {
|
auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* {
|
||||||
for (auto &field : _form.fields) {
|
for (auto &value : _form.rows) {
|
||||||
for (auto &file : field.filesInEdit) {
|
for (auto &file : value.filesInEdit) {
|
||||||
if (file.uploaded && file.uploaded->fullId == fullId) {
|
if (file.uploadData && file.uploadData->fullId == fullId) {
|
||||||
return &file;
|
return &file;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -889,8 +917,8 @@ auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto FormController::findEditFile(const FileKey &key) -> EditFile* {
|
auto FormController::findEditFile(const FileKey &key) -> EditFile* {
|
||||||
for (auto &field : _form.fields) {
|
for (auto &value : _form.rows) {
|
||||||
for (auto &file : field.filesInEdit) {
|
for (auto &file : value.filesInEdit) {
|
||||||
if (file.fields.dcId == key.dcId && file.fields.id == key.id) {
|
if (file.fields.dcId == key.dcId && file.fields.id == key.id) {
|
||||||
return &file;
|
return &file;
|
||||||
}
|
}
|
||||||
|
@ -900,11 +928,11 @@ auto FormController::findEditFile(const FileKey &key) -> EditFile* {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto FormController::findFile(const FileKey &key)
|
auto FormController::findFile(const FileKey &key)
|
||||||
-> std::pair<Field*, File*> {
|
-> std::pair<Value*, File*> {
|
||||||
for (auto &field : _form.fields) {
|
for (auto &value : _form.rows) {
|
||||||
for (auto &file : field.files) {
|
for (auto &file : value.files) {
|
||||||
if (file.dcId == key.dcId && file.id == key.id) {
|
if (file.dcId == key.dcId && file.id == key.id) {
|
||||||
return { &field, &file };
|
return { &value, &file };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -923,11 +951,11 @@ void FormController::parseForm(const MTPaccount_AuthorizationForm &result) {
|
||||||
|
|
||||||
const auto &data = result.c_account_authorizationForm();
|
const auto &data = result.c_account_authorizationForm();
|
||||||
|
|
||||||
auto values = std::vector<Field>();
|
auto values = std::vector<Value>();
|
||||||
for (const auto &value : data.vvalues.v) {
|
for (const auto &value : data.vvalues.v) {
|
||||||
values.push_back(parseValue(value));
|
values.push_back(parseValue(value));
|
||||||
}
|
}
|
||||||
const auto findValue = [&](Field::Type type) -> Field* {
|
const auto findValue = [&](Value::Type type) -> Value* {
|
||||||
for (auto &value : values) {
|
for (auto &value : values) {
|
||||||
if (value.type == type) {
|
if (value.type == type) {
|
||||||
return &value;
|
return &value;
|
||||||
|
@ -936,10 +964,9 @@ void FormController::parseForm(const MTPaccount_AuthorizationForm &result) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
_form.requestWrite = false;
|
|
||||||
App::feedUsers(data.vusers);
|
App::feedUsers(data.vusers);
|
||||||
for (const auto &required : data.vrequired_types.v) {
|
for (const auto &required : data.vrequired_types.v) {
|
||||||
using Type = Field::Type;
|
using Type = Value::Type;
|
||||||
|
|
||||||
const auto type = [&] {
|
const auto type = [&] {
|
||||||
switch (required.type()) {
|
switch (required.type()) {
|
||||||
|
@ -951,10 +978,10 @@ void FormController::parseForm(const MTPaccount_AuthorizationForm &result) {
|
||||||
Unexpected("Type in secureValueType type.");
|
Unexpected("Type in secureValueType type.");
|
||||||
}();
|
}();
|
||||||
|
|
||||||
if (auto field = findValue(type)) {
|
if (auto value = findValue(type)) {
|
||||||
_form.fields.push_back(std::move(*field));
|
_form.rows.push_back(std::move(*value));
|
||||||
} else {
|
} else {
|
||||||
_form.fields.push_back(Field(type));
|
_form.rows.push_back(Value(type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_bot = App::userLoaded(_request.botId);
|
_bot = App::userLoaded(_request.botId);
|
||||||
|
|
|
@ -83,8 +83,8 @@ public:
|
||||||
rpl::producer<QString> passwordError() const;
|
rpl::producer<QString> passwordError() const;
|
||||||
QString passwordHint() const;
|
QString passwordHint() const;
|
||||||
|
|
||||||
void uploadScan(int fieldIndex, QByteArray &&content);
|
void uploadScan(int valueIndex, QByteArray &&content);
|
||||||
void deleteScan(int fieldIndex, int fileIndex);
|
void deleteScan(int valueIndex, int fileIndex);
|
||||||
|
|
||||||
rpl::producer<> secretReadyEvents() const;
|
rpl::producer<> secretReadyEvents() const;
|
||||||
|
|
||||||
|
@ -97,15 +97,15 @@ public:
|
||||||
QString title,
|
QString title,
|
||||||
QString description,
|
QString description,
|
||||||
bool ready)> callback);
|
bool ready)> callback);
|
||||||
void editField(int index);
|
void editValue(int index);
|
||||||
|
|
||||||
void saveFieldIdentity(int index, const IdentityData &data);
|
void saveValueIdentity(int index, const IdentityData &data);
|
||||||
|
|
||||||
~FormController();
|
~FormController();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct UploadedScan {
|
struct UploadScanData {
|
||||||
~UploadedScan();
|
~UploadScanData();
|
||||||
|
|
||||||
FullMsgId fullId;
|
FullMsgId fullId;
|
||||||
uint64 fileId = 0;
|
uint64 fileId = 0;
|
||||||
|
@ -121,7 +121,10 @@ private:
|
||||||
uint64 accessHash = 0;
|
uint64 accessHash = 0;
|
||||||
int32 size = 0;
|
int32 size = 0;
|
||||||
int32 dcId = 0;
|
int32 dcId = 0;
|
||||||
bytes::vector fileHash;
|
TimeId date = 0;
|
||||||
|
bytes::vector hash;
|
||||||
|
bytes::vector secret;
|
||||||
|
bytes::vector encryptedSecret;
|
||||||
|
|
||||||
int downloadOffset = 0;
|
int downloadOffset = 0;
|
||||||
QImage image;
|
QImage image;
|
||||||
|
@ -129,17 +132,23 @@ private:
|
||||||
struct EditFile {
|
struct EditFile {
|
||||||
EditFile(
|
EditFile(
|
||||||
const File &fields,
|
const File &fields,
|
||||||
std::unique_ptr<UploadedScan> &&uploaded);
|
std::unique_ptr<UploadScanData> &&uploadData);
|
||||||
|
|
||||||
File fields;
|
File fields;
|
||||||
std::unique_ptr<UploadedScan> uploaded;
|
std::unique_ptr<UploadScanData> uploadData;
|
||||||
bool deleted = false;
|
bool deleted = false;
|
||||||
};
|
};
|
||||||
struct Verification {
|
struct Verification {
|
||||||
TimeId date;
|
TimeId date;
|
||||||
QString provider;
|
|
||||||
};
|
};
|
||||||
struct Field {
|
struct ValueData {
|
||||||
|
QByteArray original;
|
||||||
|
std::map<QString, QString> parsed;
|
||||||
|
bytes::vector hash;
|
||||||
|
bytes::vector secret;
|
||||||
|
bytes::vector encryptedSecret;
|
||||||
|
};
|
||||||
|
struct Value {
|
||||||
enum class Type {
|
enum class Type {
|
||||||
Identity,
|
Identity,
|
||||||
Address,
|
Address,
|
||||||
|
@ -147,24 +156,19 @@ private:
|
||||||
Email,
|
Email,
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit Field(Type type);
|
explicit Value(Type type);
|
||||||
Field(Field &&other) = default;
|
Value(Value &&other) = default;
|
||||||
Field &operator=(Field &&other) = default;
|
Value &operator=(Value &&other) = default;
|
||||||
|
|
||||||
Type type;
|
Type type;
|
||||||
QByteArray originalData;
|
ValueData data;
|
||||||
std::map<QString, QString> parsedData;
|
|
||||||
bytes::vector dataHash;
|
|
||||||
std::vector<File> files;
|
std::vector<File> files;
|
||||||
std::vector<EditFile> filesInEdit;
|
std::vector<EditFile> filesInEdit;
|
||||||
bytes::vector secret;
|
bytes::vector consistencyHash;
|
||||||
bytes::vector encryptedSecret;
|
|
||||||
bytes::vector hash;
|
|
||||||
base::optional<Verification> verification;
|
base::optional<Verification> verification;
|
||||||
};
|
};
|
||||||
struct Form {
|
struct Form {
|
||||||
bool requestWrite = false;
|
std::vector<Value> rows;
|
||||||
std::vector<Field> fields;
|
|
||||||
};
|
};
|
||||||
struct PasswordSettings {
|
struct PasswordSettings {
|
||||||
bytes::vector salt;
|
bytes::vector salt;
|
||||||
|
@ -178,7 +182,7 @@ private:
|
||||||
|
|
||||||
EditFile *findEditFile(const FullMsgId &fullId);
|
EditFile *findEditFile(const FullMsgId &fullId);
|
||||||
EditFile *findEditFile(const FileKey &key);
|
EditFile *findEditFile(const FileKey &key);
|
||||||
std::pair<Field*, File*> findFile(const FileKey &key);
|
std::pair<Value*, File*> findFile(const FileKey &key);
|
||||||
|
|
||||||
void requestForm();
|
void requestForm();
|
||||||
void requestPassword();
|
void requestPassword();
|
||||||
|
@ -187,20 +191,23 @@ private:
|
||||||
void formFail(const RPCError &error);
|
void formFail(const RPCError &error);
|
||||||
void parseForm(const MTPaccount_AuthorizationForm &result);
|
void parseForm(const MTPaccount_AuthorizationForm &result);
|
||||||
void showForm();
|
void showForm();
|
||||||
Field parseValue(const MTPSecureValue &value) const;
|
Value parseValue(const MTPSecureValue &value) const;
|
||||||
template <typename DataType>
|
template <typename DataType>
|
||||||
Field parseEncryptedField(
|
Value parseEncryptedValue(
|
||||||
Field::Type type,
|
Value::Type type,
|
||||||
const DataType &data) const;
|
const DataType &data) const;
|
||||||
template <typename DataType>
|
template <typename DataType>
|
||||||
Field parsePlainTextField(
|
Value parsePlainTextValue(
|
||||||
Field::Type type,
|
Value::Type type,
|
||||||
const QByteArray &value,
|
const QByteArray &text,
|
||||||
const DataType &data) const;
|
const DataType &data) const;
|
||||||
Verification parseVerified(const MTPSecureValueVerified &data) const;
|
Verification parseVerified(const MTPSecureValueVerified &data) const;
|
||||||
std::vector<File> parseFiles(
|
std::vector<File> parseFiles(
|
||||||
const QVector<MTPSecureFile> &data,
|
const QVector<MTPSecureFile> &data,
|
||||||
const std::vector<EditFile> &editData = {}) const;
|
const std::vector<EditFile> &editData = {}) const;
|
||||||
|
void fillDownloadedFile(
|
||||||
|
File &destination,
|
||||||
|
const std::vector<EditFile> &source) const;
|
||||||
|
|
||||||
void passwordDone(const MTPaccount_Password &result);
|
void passwordDone(const MTPaccount_Password &result);
|
||||||
void passwordFail(const RPCError &error);
|
void passwordFail(const RPCError &error);
|
||||||
|
@ -211,13 +218,13 @@ private:
|
||||||
bytes::const_span salt,
|
bytes::const_span salt,
|
||||||
bytes::const_span encryptedSecret,
|
bytes::const_span encryptedSecret,
|
||||||
bytes::const_span password);
|
bytes::const_span password);
|
||||||
void decryptFields();
|
void decryptValues();
|
||||||
void decryptField(Field &field);
|
void decryptValue(Value &value);
|
||||||
bool validateFieldSecret(Field &field);
|
bool validateValueSecrets(Value &value);
|
||||||
void resetField(Field &field);
|
void resetValue(Value &value);
|
||||||
|
|
||||||
IdentityData fieldDataIdentity(const Field &field) const;
|
IdentityData valueDataIdentity(const Value &value) const;
|
||||||
std::vector<ScanInfo> fieldFilesIdentity(const Field &field) const;
|
std::vector<ScanInfo> valueFilesIdentity(const Value &value) const;
|
||||||
void saveIdentity(int index);
|
void saveIdentity(int index);
|
||||||
|
|
||||||
void loadFiles(std::vector<File> &files);
|
void loadFiles(std::vector<File> &files);
|
||||||
|
@ -226,17 +233,15 @@ private:
|
||||||
void fileLoadFail(FileKey key);
|
void fileLoadFail(FileKey key);
|
||||||
void generateSecret(bytes::const_span password);
|
void generateSecret(bytes::const_span password);
|
||||||
|
|
||||||
template <typename FileHashes>
|
|
||||||
bytes::vector computeFilesHash(
|
|
||||||
FileHashes fileHashes,
|
|
||||||
bytes::const_span valueHash);
|
|
||||||
|
|
||||||
void subscribeToUploader();
|
void subscribeToUploader();
|
||||||
void encryptScan(
|
void encryptScan(
|
||||||
int fieldIndex,
|
int valueIndex,
|
||||||
int fileIndex,
|
int fileIndex,
|
||||||
QByteArray &&content);
|
QByteArray &&content);
|
||||||
void uploadEncryptedScan(int fieldIndex, int fileIndex, UploadedScan &&data);
|
void uploadEncryptedScan(
|
||||||
|
int valueIndex,
|
||||||
|
int fileIndex,
|
||||||
|
UploadScanData &&data);
|
||||||
void scanUploadDone(const Storage::UploadSecureDone &data);
|
void scanUploadDone(const Storage::UploadSecureDone &data);
|
||||||
void scanUploadProgress(const Storage::UploadSecureProgress &data);
|
void scanUploadProgress(const Storage::UploadSecureProgress &data);
|
||||||
void scanUploadFail(const FullMsgId &fullId);
|
void scanUploadFail(const FullMsgId &fullId);
|
||||||
|
@ -256,6 +261,7 @@ private:
|
||||||
rpl::event_stream<ScanInfo> _scanUpdated;
|
rpl::event_stream<ScanInfo> _scanUpdated;
|
||||||
|
|
||||||
bytes::vector _secret;
|
bytes::vector _secret;
|
||||||
|
uint64 _secretId = 0;
|
||||||
std::vector<base::lambda<void()>> _secretCallbacks;
|
std::vector<base::lambda<void()>> _secretCallbacks;
|
||||||
mtpRequestId _saveSecretRequestId = 0;
|
mtpRequestId _saveSecretRequestId = 0;
|
||||||
rpl::event_stream<> _secretReady;
|
rpl::event_stream<> _secretReady;
|
||||||
|
|
Loading…
Reference in New Issue