mirror of https://github.com/procxx/kepka.git
Update API scheme.
This commit is contained in:
parent
1392e05ab1
commit
083b520eee
|
@ -281,6 +281,8 @@ 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;
|
||||||
|
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;
|
||||||
|
|
||||||
|
@ -584,12 +586,12 @@ authorization#7bf2e6f6 hash:long flags:int device_model:string platform:string s
|
||||||
|
|
||||||
account.authorizations#1250abde authorizations:Vector<Authorization> = account.Authorizations;
|
account.authorizations#1250abde authorizations:Vector<Authorization> = account.Authorizations;
|
||||||
|
|
||||||
account.noPassword#c86371bd new_salt:bytes secret_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#49efccf5 current_salt:bytes new_salt:bytes secret_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#f8ebfc3f email:string secure_secret:bytes = account.PasswordSettings;
|
account.passwordSettings#48ec1750 email:string secure_salt:bytes secure_secret:bytes secure_secret_hash:long = account.PasswordSettings;
|
||||||
|
|
||||||
account.passwordInputSettings#20ce9e59 flags:# new_salt:flags.0?bytes new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_secret:flags.2?bytes = account.PasswordInputSettings;
|
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;
|
||||||
|
|
||||||
auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery;
|
auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery;
|
||||||
|
|
||||||
|
@ -965,36 +967,40 @@ 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:string = InputSecureFile;
|
inputSecureFileUploaded#c53ed020 id:long parts:int md5_checksum:string file_hash: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:string = SecureFile;
|
secureFile#40ad69ba id:long access_hash:long size:int dc_id:int file_hash:bytes = SecureFile;
|
||||||
|
|
||||||
secureValueEmpty#f0cec6e0 name:string = SecureValue;
|
secureData#262b0134 data:bytes data_hash:bytes = SecureData;
|
||||||
secureValueData#6c3bf275 name:string data:bytes hash:string secret:bytes = SecureValue;
|
|
||||||
secureValueFile#a145a158 name:string file:Vector<SecureFile> hash:string secret:bytes = SecureValue;
|
|
||||||
secureValueText#1bb37ce3 name:string text:string hash:string = SecureValue;
|
|
||||||
|
|
||||||
inputSecureValueData#627bcf0d name:string data:bytes hash:string secret:bytes = InputSecureValue;
|
secureValueVerified#612d86df date:int provider:string = SecureValueVerified;
|
||||||
inputSecureValueFile#e24f9466 name:string file:Vector<InputSecureFile> hash:string secret:bytes = InputSecureValue;
|
|
||||||
inputSecureValueText#955a14bb name:string text:string hash:string = InputSecureValue;
|
|
||||||
|
|
||||||
secureValueHash#e4c2677 name:string hash:string = SecureValueHash;
|
secureValueTypeIdentity#37da58ca = SecureValueType;
|
||||||
|
secureValueTypeAddress#cbe31e26 = SecureValueType;
|
||||||
|
secureValueTypePhone#b320aadb = SecureValueType;
|
||||||
|
secureValueTypeEmail#8e3ca7ee = SecureValueType;
|
||||||
|
|
||||||
authFieldTypeIdentity#4413b5d = AuthFieldType;
|
secureValueIdentity#a6c927ad flags:# data:SecureData files:Vector<SecureFile> secret:bytes hash:bytes verified:flags.0?SecureValueVerified = SecureValue;
|
||||||
authFieldTypeAddress#63bbccf3 = AuthFieldType;
|
secureValueAddress#74a0d79c flags:# data:SecureData files:Vector<SecureFile> secret:bytes hash:bytes verified:flags.0?SecureValueVerified = SecureValue;
|
||||||
authFieldTypePhone#b56120ff = AuthFieldType;
|
secureValuePhone#a1ca84fe flags:# phone:string hash:bytes verified:flags.0?SecureValueVerified = SecureValue;
|
||||||
authFieldTypeEmail#4cc9fd06 = AuthFieldType;
|
secureValueEmail#c4db6579 flags:# email:string hash:bytes verified:flags.0?SecureValueVerified = SecureValue;
|
||||||
|
|
||||||
authField#ed81c3dc flags:# type:AuthFieldType data:SecureValue document:flags.0?SecureValue = AuthField;
|
inputSecureValueIdentity#ae27d606 data:SecureData files:Vector<InputSecureFile> secret:bytes hash:bytes = InputSecureValue;
|
||||||
|
inputSecureValueAddress#c1f733e5 data:SecureData files:Vector<InputSecureFile> secret:bytes hash:bytes = InputSecureValue;
|
||||||
|
inputSecureValuePhone#141e00b8 phone:string hash:bytes = InputSecureValue;
|
||||||
|
inputSecureValueEmail#2dc15b9a email:string hash:bytes = InputSecureValue;
|
||||||
|
|
||||||
account.secureValueResultSaved#f4e0ab11 = account.SecureValueResult;
|
secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash;
|
||||||
account.secureValueVerificationNeeded#9192a1db code_hash:string = account.SecureValueResult;
|
|
||||||
|
|
||||||
account.authorizationForm#40997b71 flags:# accepted:flags.0?true request_write:flags.1?true bot_id:int fields:Vector<AuthField> accepted_fields:Vector<AuthFieldType> users:Vector<User> = account.AuthorizationForm;
|
secureValueSaved#c9147049 files:Vector<SecureFile> = SecureValueSaved;
|
||||||
|
|
||||||
account.authorizationResult#836727ce user_data:DataJSON accepted_fields:Vector<AuthFieldType> = account.AuthorizationResult;
|
secureCredentialsEncrypted#628fe12a data:bytes secret:bytes hash:bytes = SecureCredentialsEncrypted;
|
||||||
|
|
||||||
|
account.authorizationForm#8d9dddeb flags:# accepted:flags.0?true required_types:Vector<SecureValueType> values:Vector<SecureValue> users:Vector<User> = account.AuthorizationForm;
|
||||||
|
|
||||||
|
account.sentEmailCode#28b1633b email_pattern:string = account.SentEmailCode;
|
||||||
|
|
||||||
---functions---
|
---functions---
|
||||||
|
|
||||||
|
@ -1051,11 +1057,15 @@ account.getTmpPassword#4a82327e password_hash:bytes period:int = account.TmpPass
|
||||||
account.getWebAuthorizations#182e6d6f = account.WebAuthorizations;
|
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#d86fa683 user_id:InputUser name:Vector<string> = Vector<SecureValue>;
|
account.getSecureValue#d97e77cb user_id:InputUser types:Vector<SecureValueType> = Vector<SecureValue>;
|
||||||
account.saveSecureValue#9fc2a624 data:InputSecureValue = account.SecureValueResult;
|
account.saveSecureValue#842659a5 value:InputSecureValue secure_secret_hash:long = SecureValueSaved;
|
||||||
account.verifySecureValue#68367bed name:string code_hash:string code:string = Bool;
|
account.deleteSecureValue#b880bc4b types:Vector<SecureValueType> = Bool;
|
||||||
account.getAuthorizationForm#2f003837 flags:# bot_id:int scope:Vector<string> origin:flags.0?string package_name:flags.1?string bundle_id:flags.2?string public_key:flags.3?string = account.AuthorizationForm;
|
account.getAuthorizationForm#b86ba8e1 bot_id:int scope:string public_key:string = account.AuthorizationForm;
|
||||||
account.acceptAuthorization#df98a3f5 flags:# request_write:flags.4?true bot_id:int scope:Vector<string> origin:flags.0?string package_name:flags.1?string bundle_id:flags.2?string public_key:flags.3?string accepted_fields:Vector<AuthFieldType> value_hashes:Vector<secureValueHash> = account.AuthorizationResult;
|
account.acceptAuthorization#e7027c94 bot_id:int scope:string public_key:string value_hashes:Vector<SecureValueHash> credentials:SecureCredentialsEncrypted = Bool;
|
||||||
|
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.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode;
|
||||||
|
account.verifyEmail#ecba39db email:string code:string = Bool;
|
||||||
|
|
||||||
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
users.getUsers#d91a548 id:Vector<InputUser> = Vector<User>;
|
||||||
users.getFullUser#ca30a5b1 id:InputUser = UserFull;
|
users.getFullUser#ca30a5b1 id:InputUser = UserFull;
|
||||||
|
|
|
@ -331,6 +331,8 @@ void PasscodeBox::save(bool force) {
|
||||||
flags |= MTPDaccount_passwordInputSettings::Flag::f_email;
|
flags |= MTPDaccount_passwordInputSettings::Flag::f_email;
|
||||||
}
|
}
|
||||||
const auto newSecureSecret = bytes::vector();
|
const auto newSecureSecret = bytes::vector();
|
||||||
|
const auto newSecureSalt = bytes::vector();
|
||||||
|
const auto newSecureSecretHash = 0ULL;
|
||||||
_setRequest = MTP::send(
|
_setRequest = MTP::send(
|
||||||
MTPaccount_UpdatePasswordSettings(
|
MTPaccount_UpdatePasswordSettings(
|
||||||
MTP_bytes(oldPasswordHash),
|
MTP_bytes(oldPasswordHash),
|
||||||
|
@ -340,7 +342,9 @@ void PasscodeBox::save(bool force) {
|
||||||
MTP_bytes(newPasswordHash),
|
MTP_bytes(newPasswordHash),
|
||||||
MTP_string(hint),
|
MTP_string(hint),
|
||||||
MTP_string(email),
|
MTP_string(email),
|
||||||
MTP_bytes(newSecureSecret))),
|
MTP_bytes(newSecureSalt),
|
||||||
|
MTP_bytes(newSecureSecret),
|
||||||
|
MTP_long(newSecureSecretHash))),
|
||||||
rpcDone(&PasscodeBox::setPasswordDone),
|
rpcDone(&PasscodeBox::setPasswordDone),
|
||||||
rpcFail(&PasscodeBox::setPasswordFail));
|
rpcFail(&PasscodeBox::setPasswordFail));
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,6 +166,11 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto prepareSecureValuesSent = [&](const MTPDmessageActionSecureValuesSent &action) {
|
||||||
|
// #TODO passport
|
||||||
|
return PreparedText{ QString("Secure values sent.") };
|
||||||
|
};
|
||||||
|
|
||||||
auto messageText = PreparedText {};
|
auto messageText = PreparedText {};
|
||||||
|
|
||||||
switch (action.type()) {
|
switch (action.type()) {
|
||||||
|
@ -187,6 +192,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
||||||
case mtpc_messageActionScreenshotTaken: messageText = prepareScreenshotTaken(); break;
|
case mtpc_messageActionScreenshotTaken: messageText = prepareScreenshotTaken(); break;
|
||||||
case mtpc_messageActionCustomAction: messageText = prepareCustomAction(action.c_messageActionCustomAction()); break;
|
case mtpc_messageActionCustomAction: messageText = prepareCustomAction(action.c_messageActionCustomAction()); break;
|
||||||
case mtpc_messageActionBotAllowed: messageText = prepareBotAllowed(action.c_messageActionBotAllowed()); break;
|
case mtpc_messageActionBotAllowed: messageText = prepareBotAllowed(action.c_messageActionBotAllowed()); break;
|
||||||
|
case mtpc_messageActionSecureValuesSent: messageText = prepareSecureValuesSent(action.c_messageActionSecureValuesSent()); break;
|
||||||
default: messageText.text = lang(lng_message_empty); break;
|
default: messageText.text = lang(lng_message_empty); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -914,7 +914,7 @@ bool Messenger::openLocalUrl(const QString &url) {
|
||||||
authMatch->captured(1),
|
authMatch->captured(1),
|
||||||
UrlParamNameTransform::ToLower);
|
UrlParamNameTransform::ToLower);
|
||||||
if (const auto botId = params.value("bot_id", QString()).toInt()) {
|
if (const auto botId = params.value("bot_id", QString()).toInt()) {
|
||||||
const auto scope = params.value("scope", QString()).split(',');
|
const auto scope = params.value("scope", QString());
|
||||||
const auto callback = params.value("callback_url", QString());
|
const auto callback = params.value("callback_url", QString());
|
||||||
const auto publicKey = params.value("public_key", QString());
|
const auto publicKey = params.value("public_key", QString());
|
||||||
if (const auto window = App::wnd()) {
|
if (const auto window = App::wnd()) {
|
||||||
|
|
|
@ -146,13 +146,19 @@ bytes::vector EncryptSecretBytes(
|
||||||
return Encrypt(secret, std::move(params));
|
return Encrypt(secret, std::move(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes::vector Concatenate(
|
bytes::vector DecryptSecureSecret(
|
||||||
bytes::const_span a,
|
bytes::const_span salt,
|
||||||
bytes::const_span b) {
|
bytes::const_span encryptedSecret,
|
||||||
auto result = bytes::vector(a.size() + b.size());
|
bytes::const_span password) {
|
||||||
bytes::copy(result, a);
|
Expects(!salt.empty());
|
||||||
bytes::copy(gsl::make_span(result).subspan(a.size()), b);
|
Expects(!encryptedSecret.empty());
|
||||||
return result;
|
Expects(!password.empty());
|
||||||
|
|
||||||
|
const auto hash = openssl::Sha512(bytes::concatenate(
|
||||||
|
salt,
|
||||||
|
password,
|
||||||
|
salt));
|
||||||
|
return DecryptSecretBytes(encryptedSecret, hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes::vector SerializeData(const std::map<QString, QString> &data) {
|
bytes::vector SerializeData(const std::map<QString, QString> &data) {
|
||||||
|
@ -246,8 +252,9 @@ 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(
|
const auto dataSecretHash = openssl::Sha512(bytes::concatenate(
|
||||||
Concatenate(dataSecret, dataHash));
|
dataSecret,
|
||||||
|
dataHash));
|
||||||
|
|
||||||
auto params = PrepareAesParams(dataSecretHash);
|
auto params = PrepareAesParams(dataSecretHash);
|
||||||
return {
|
return {
|
||||||
|
@ -272,8 +279,9 @@ bytes::vector DecryptData(
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto dataSecretHash = openssl::Sha512(
|
const auto dataSecretHash = openssl::Sha512(bytes::concatenate(
|
||||||
Concatenate(dataSecret, dataHash));
|
dataSecret,
|
||||||
|
dataHash));
|
||||||
auto params = PrepareAesParams(dataSecretHash);
|
auto params = PrepareAesParams(dataSecretHash);
|
||||||
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) {
|
||||||
|
@ -294,22 +302,15 @@ bytes::vector DecryptData(
|
||||||
bytes::vector PrepareValueHash(
|
bytes::vector PrepareValueHash(
|
||||||
bytes::const_span dataHash,
|
bytes::const_span dataHash,
|
||||||
bytes::const_span valueSecret) {
|
bytes::const_span valueSecret) {
|
||||||
const auto result = openssl::Sha256(Concatenate(dataHash, valueSecret));
|
return openssl::Sha256(bytes::concatenate(dataHash, valueSecret));
|
||||||
return { result.begin(), result.end() };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes::vector PrepareFilesHash(
|
bytes::vector PrepareFilesHash(
|
||||||
gsl::span<bytes::const_span> fileHashes,
|
gsl::span<bytes::const_span> fileHashes,
|
||||||
bytes::const_span valueSecret) {
|
bytes::const_span valueSecret) {
|
||||||
auto resultInner = bytes::vector{
|
return openssl::Sha256(bytes::concatenate(
|
||||||
valueSecret.begin(),
|
bytes::concatenate(fileHashes),
|
||||||
valueSecret.end()
|
valueSecret));
|
||||||
};
|
|
||||||
for (const auto &hash : base::reversed(fileHashes)) {
|
|
||||||
resultInner = Concatenate(hash, resultInner);
|
|
||||||
}
|
|
||||||
const auto result = openssl::Sha256(resultInner);
|
|
||||||
return { result.begin(), result.end() };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes::vector EncryptValueSecret(
|
bytes::vector EncryptValueSecret(
|
||||||
|
@ -317,7 +318,7 @@ bytes::vector EncryptValueSecret(
|
||||||
bytes::const_span secret,
|
bytes::const_span secret,
|
||||||
bytes::const_span valueHash) {
|
bytes::const_span valueHash) {
|
||||||
const auto valueSecretHash = openssl::Sha512(
|
const auto valueSecretHash = openssl::Sha512(
|
||||||
Concatenate(secret, valueHash));
|
bytes::concatenate(secret, valueHash));
|
||||||
return EncryptSecretBytes(valueSecret, valueSecretHash);
|
return EncryptSecretBytes(valueSecret, valueSecretHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,9 +326,18 @@ 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(
|
const auto valueSecretHash = openssl::Sha512(bytes::concatenate(
|
||||||
Concatenate(secret, valueHash));
|
secret,
|
||||||
|
valueHash));
|
||||||
return DecryptSecretBytes(encrypted, valueSecretHash);
|
return DecryptSecretBytes(encrypted, valueSecretHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64 CountSecureSecretHash(bytes::const_span secret) {
|
||||||
|
const auto full = openssl::Sha256(secret);
|
||||||
|
const auto part = bytes::make_span(full).subspan(
|
||||||
|
full.size() - sizeof(uint64),
|
||||||
|
sizeof(uint64));
|
||||||
|
return *reinterpret_cast<const uint64*>(part.data());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Passport
|
} // namespace Passport
|
||||||
|
|
|
@ -18,6 +18,11 @@ bytes::vector DecryptSecretBytes(
|
||||||
bytes::const_span encryptedSecret,
|
bytes::const_span encryptedSecret,
|
||||||
bytes::const_span passwordHashForSecret);
|
bytes::const_span passwordHashForSecret);
|
||||||
|
|
||||||
|
bytes::vector DecryptSecureSecret(
|
||||||
|
bytes::const_span salt,
|
||||||
|
bytes::const_span encryptedSecret,
|
||||||
|
bytes::const_span password);
|
||||||
|
|
||||||
bytes::vector PasswordHashForSecret(bytes::const_span passwordUtf8);
|
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);
|
||||||
|
@ -58,4 +63,6 @@ bytes::vector PrepareFilesHash(
|
||||||
gsl::span<bytes::const_span> fileHashes,
|
gsl::span<bytes::const_span> fileHashes,
|
||||||
bytes::const_span valueSecret);
|
bytes::const_span valueSecret);
|
||||||
|
|
||||||
|
uint64 CountSecureSecretHash(bytes::const_span secret);
|
||||||
|
|
||||||
} // namespace Passport
|
} // namespace Passport
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace Passport {
|
||||||
|
|
||||||
FormRequest::FormRequest(
|
FormRequest::FormRequest(
|
||||||
UserId botId,
|
UserId botId,
|
||||||
const QStringList &scope,
|
const QString &scope,
|
||||||
const QString &callbackUrl,
|
const QString &callbackUrl,
|
||||||
const QString &publicKey)
|
const QString &publicKey)
|
||||||
: botId(botId)
|
: botId(botId)
|
||||||
|
@ -65,8 +65,6 @@ FormController::FormController(
|
||||||
const FormRequest &request)
|
const FormRequest &request)
|
||||||
: _controller(controller)
|
: _controller(controller)
|
||||||
, _request(request) {
|
, _request(request) {
|
||||||
const auto url = QUrl(_request.callbackUrl);
|
|
||||||
_origin = url.scheme() + "://" + url.host();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::show() {
|
void FormController::show() {
|
||||||
|
@ -74,8 +72,16 @@ void FormController::show() {
|
||||||
requestPassword();
|
requestPassword();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bytes::vector FormController::passwordHashForAuth(
|
||||||
|
bytes::const_span password) const {
|
||||||
|
return openssl::Sha256(bytes::concatenate(
|
||||||
|
_password.salt,
|
||||||
|
password,
|
||||||
|
_password.salt));
|
||||||
|
}
|
||||||
|
|
||||||
void FormController::submitPassword(const QString &password) {
|
void FormController::submitPassword(const QString &password) {
|
||||||
Expects(!_password.salt.isEmpty());
|
Expects(!_password.salt.empty());
|
||||||
|
|
||||||
if (_passwordCheckRequestId) {
|
if (_passwordCheckRequestId) {
|
||||||
return;
|
return;
|
||||||
|
@ -83,41 +89,19 @@ void FormController::submitPassword(const QString &password) {
|
||||||
_passwordError.fire(QString());
|
_passwordError.fire(QString());
|
||||||
}
|
}
|
||||||
const auto passwordBytes = password.toUtf8();
|
const auto passwordBytes = password.toUtf8();
|
||||||
_passwordHashForAuth = openssl::Sha256(bytes::concatenate(
|
|
||||||
bytes::make_span(_password.salt),
|
|
||||||
bytes::make_span(passwordBytes),
|
|
||||||
bytes::make_span(_password.salt)));
|
|
||||||
_passwordCheckRequestId = request(MTPaccount_GetPasswordSettings(
|
_passwordCheckRequestId = request(MTPaccount_GetPasswordSettings(
|
||||||
MTP_bytes(_passwordHashForAuth)
|
MTP_bytes(passwordHashForAuth(bytes::make_span(passwordBytes)))
|
||||||
)).handleFloodErrors(
|
)).handleFloodErrors(
|
||||||
).done([=](const MTPaccount_PasswordSettings &result) {
|
).done([=](const MTPaccount_PasswordSettings &result) {
|
||||||
Expects(result.type() == mtpc_account_passwordSettings);
|
Expects(result.type() == mtpc_account_passwordSettings);
|
||||||
|
|
||||||
_passwordCheckRequestId = 0;
|
_passwordCheckRequestId = 0;
|
||||||
const auto &data = result.c_account_passwordSettings();
|
const auto &data = result.c_account_passwordSettings();
|
||||||
_passwordEmail = qs(data.vemail);
|
_password.confirmedEmail = qs(data.vemail);
|
||||||
_passwordHashForSecret = openssl::Sha512(bytes::make_span(passwordBytes));
|
validateSecureSecret(
|
||||||
_secret = DecryptSecretBytes(
|
bytes::make_span(data.vsecure_salt.v),
|
||||||
bytes::make_span(data.vsecure_secret.v),
|
bytes::make_span(data.vsecure_secret.v),
|
||||||
_passwordHashForSecret);
|
bytes::make_span(passwordBytes));
|
||||||
for (auto &field : _form.fields) {
|
|
||||||
field.data.values = fillData(field.data);
|
|
||||||
if (auto &document = field.document) {
|
|
||||||
const auto filesHash = bytes::make_span(document->filesHash);
|
|
||||||
document->filesSecret = DecryptValueSecret(
|
|
||||||
bytes::make_span(document->filesSecretEncrypted),
|
|
||||||
_secret,
|
|
||||||
filesHash);
|
|
||||||
if (document->filesSecret.empty()
|
|
||||||
&& !document->files.empty()) {
|
|
||||||
LOG(("API Error: Empty decrypted files secret. "
|
|
||||||
"Forgetting files."));
|
|
||||||
document->files.clear();
|
|
||||||
document->filesHash.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_secretReady.fire({});
|
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
_passwordCheckRequestId = 0;
|
_passwordCheckRequestId = 0;
|
||||||
if (MTP::isFloodError(error)) {
|
if (MTP::isFloodError(error)) {
|
||||||
|
@ -130,6 +114,87 @@ void FormController::submitPassword(const QString &password) {
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FormController::validateSecureSecret(
|
||||||
|
bytes::const_span salt,
|
||||||
|
bytes::const_span encryptedSecret,
|
||||||
|
bytes::const_span password) {
|
||||||
|
if (!salt.empty() && !encryptedSecret.empty()) {
|
||||||
|
_secret = DecryptSecureSecret(salt, encryptedSecret, password);
|
||||||
|
if (_secret.empty()) {
|
||||||
|
LOG(("API Error: Failed to decrypt secure secret. "
|
||||||
|
"Forgetting all files and data :("));
|
||||||
|
for (auto &field : _form.fields) {
|
||||||
|
if (!field.encryptedSecret.empty()) {
|
||||||
|
resetField(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
decryptFields();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_secret.empty()) {
|
||||||
|
generateSecret(password);
|
||||||
|
}
|
||||||
|
_secretReady.fire({});
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormController::decryptFields() {
|
||||||
|
Expects(!_secret.empty());
|
||||||
|
|
||||||
|
for (auto &field : _form.fields) {
|
||||||
|
if (field.encryptedSecret.empty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
decryptField(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormController::decryptField(Field &field) {
|
||||||
|
Expects(!_secret.empty());
|
||||||
|
Expects(!field.encryptedSecret.empty());
|
||||||
|
|
||||||
|
if (!validateFieldSecret(field)) {
|
||||||
|
resetField(field);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto dataHash = bytes::make_span(field.dataHash);
|
||||||
|
field.parsedData = DeserializeData(DecryptData(
|
||||||
|
bytes::make_span(field.originalData),
|
||||||
|
field.dataHash,
|
||||||
|
field.secret));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FormController::validateFieldSecret(Field &field) {
|
||||||
|
field.secret = DecryptValueSecret(
|
||||||
|
field.encryptedSecret,
|
||||||
|
_secret,
|
||||||
|
field.hash);
|
||||||
|
if (field.secret.empty()) {
|
||||||
|
LOG(("API Error: Could not decrypt value secret. "
|
||||||
|
"Forgetting files and data :("));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto fileHashes = ranges::view::all(
|
||||||
|
field.files
|
||||||
|
) | ranges::view::transform([](File &file) {
|
||||||
|
return bytes::make_span(file.fileHash);
|
||||||
|
});
|
||||||
|
const auto countedHash = openssl::Sha256(bytes::concatenate(
|
||||||
|
field.dataHash,
|
||||||
|
bytes::concatenate(fileHashes),
|
||||||
|
field.secret));
|
||||||
|
if (field.hash != countedHash) {
|
||||||
|
LOG(("API Error: Wrong hash after decrypting value secret. "
|
||||||
|
"Forgetting files and data :("));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormController::resetField(Field &field) {
|
||||||
|
field = Field(field.type);
|
||||||
|
}
|
||||||
|
|
||||||
rpl::producer<QString> FormController::passwordError() const {
|
rpl::producer<QString> FormController::passwordError() const {
|
||||||
return _passwordError.events();
|
return _passwordError.events();
|
||||||
}
|
}
|
||||||
|
@ -141,22 +206,21 @@ QString FormController::passwordHint() const {
|
||||||
void FormController::uploadScan(int index, QByteArray &&content) {
|
void FormController::uploadScan(int index, QByteArray &&content) {
|
||||||
Expects(_editBox != nullptr);
|
Expects(_editBox != nullptr);
|
||||||
Expects(index >= 0 && index < _form.fields.size());
|
Expects(index >= 0 && index < _form.fields.size());
|
||||||
Expects(_form.fields[index].document.has_value());
|
|
||||||
|
|
||||||
auto &document = *_form.fields[index].document;
|
auto &field = _form.fields[index];
|
||||||
if (document.filesSecret.empty()) {
|
if (field.secret.empty()) {
|
||||||
document.filesSecret = GenerateSecretBytes();
|
field.secret = GenerateSecretBytes();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto weak = _editBox;
|
const auto weak = _editBox;
|
||||||
crl::async([
|
crl::async([
|
||||||
=,
|
=,
|
||||||
bytes = std::move(content),
|
bytes = std::move(content),
|
||||||
filesSecret = document.filesSecret
|
valueSecret = field.secret
|
||||||
] {
|
] {
|
||||||
auto data = EncryptData(
|
auto data = EncryptData(
|
||||||
bytes::make_span(bytes),
|
bytes::make_span(bytes),
|
||||||
filesSecret);
|
valueSecret);
|
||||||
auto result = UploadedScan();
|
auto result = UploadedScan();
|
||||||
result.fileId = rand_value<uint64>();
|
result.fileId = rand_value<uint64>();
|
||||||
result.hash = std::move(data.hash);
|
result.hash = std::move(data.hash);
|
||||||
|
@ -193,14 +257,13 @@ void FormController::subscribeToUploader() {
|
||||||
void FormController::uploadEncryptedScan(int index, UploadedScan &&data) {
|
void FormController::uploadEncryptedScan(int index, UploadedScan &&data) {
|
||||||
Expects(_editBox != nullptr);
|
Expects(_editBox != nullptr);
|
||||||
Expects(index >= 0 && index < _form.fields.size());
|
Expects(index >= 0 && index < _form.fields.size());
|
||||||
Expects(_form.fields[index].document.has_value());
|
|
||||||
|
|
||||||
subscribeToUploader();
|
subscribeToUploader();
|
||||||
|
|
||||||
_form.fields[index].document->filesInEdit.emplace_back(
|
_form.fields[index].filesInEdit.emplace_back(
|
||||||
File(),
|
File(),
|
||||||
std::make_unique<UploadedScan>(std::move(data)));
|
std::make_unique<UploadedScan>(std::move(data)));
|
||||||
auto &file = _form.fields[index].document->filesInEdit.back();
|
auto &file = _form.fields[index].filesInEdit.back();
|
||||||
|
|
||||||
auto uploaded = std::make_shared<FileLoadResult>(
|
auto uploaded = std::make_shared<FileLoadResult>(
|
||||||
TaskId(),
|
TaskId(),
|
||||||
|
@ -238,7 +301,7 @@ rpl::producer<> FormController::secretReadyEvents() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FormController::defaultEmail() const {
|
QString FormController::defaultEmail() const {
|
||||||
return _passwordEmail;
|
return _password.confirmedEmail;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FormController::defaultPhoneNumber() const {
|
QString FormController::defaultPhoneNumber() const {
|
||||||
|
@ -294,13 +357,9 @@ void FormController::editField(int index) {
|
||||||
auto &field = _form.fields[index];
|
auto &field = _form.fields[index];
|
||||||
switch (field.type) {
|
switch (field.type) {
|
||||||
case Field::Type::Identity:
|
case Field::Type::Identity:
|
||||||
if (field.document) {
|
loadFiles(field.files);
|
||||||
loadFiles(field.document->files);
|
field.filesInEdit = ranges::view::all(
|
||||||
} else {
|
field.files
|
||||||
field.document = Value();
|
|
||||||
}
|
|
||||||
field.document->filesInEdit = ranges::view::all(
|
|
||||||
field.document->files
|
|
||||||
) | ranges::view::transform([](const File &file) {
|
) | ranges::view::transform([](const File &file) {
|
||||||
return EditFile(file, nullptr);
|
return EditFile(file, nullptr);
|
||||||
}) | ranges::to_vector;
|
}) | ranges::to_vector;
|
||||||
|
@ -353,7 +412,7 @@ void FormController::fileLoaded(FileKey key, const QByteArray &bytes) {
|
||||||
const auto decrypted = DecryptData(
|
const auto decrypted = DecryptData(
|
||||||
bytes::make_span(bytes),
|
bytes::make_span(bytes),
|
||||||
file->fileHash,
|
file->fileHash,
|
||||||
field->document->filesSecret);
|
field->secret);
|
||||||
auto image = App::readImage(QByteArray::fromRawData(
|
auto image = App::readImage(QByteArray::fromRawData(
|
||||||
reinterpret_cast<const char*>(decrypted.data()),
|
reinterpret_cast<const char*>(decrypted.data()),
|
||||||
decrypted.size()));
|
decrypted.size()));
|
||||||
|
@ -368,7 +427,7 @@ void FormController::fileLoaded(FileKey key, const QByteArray &bytes) {
|
||||||
}
|
}
|
||||||
|
|
||||||
IdentityData FormController::fieldDataIdentity(const Field &field) const {
|
IdentityData FormController::fieldDataIdentity(const Field &field) const {
|
||||||
const auto &map = field.data.values;
|
const auto &map = field.parsedData;
|
||||||
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;
|
||||||
|
@ -381,10 +440,8 @@ IdentityData FormController::fieldDataIdentity(const Field &field) const {
|
||||||
|
|
||||||
std::vector<ScanInfo> FormController::fieldFilesIdentity(
|
std::vector<ScanInfo> FormController::fieldFilesIdentity(
|
||||||
const Field &field) const {
|
const Field &field) const {
|
||||||
Expects(field.document.has_value());
|
|
||||||
|
|
||||||
auto result = std::vector<ScanInfo>();
|
auto result = std::vector<ScanInfo>();
|
||||||
for (const auto &file : field.document->filesInEdit) {
|
for (const auto &file : field.filesInEdit) {
|
||||||
result.push_back({
|
result.push_back({
|
||||||
FileKey{ file.fields.id, file.fields.dcId },
|
FileKey{ file.fields.id, file.fields.dcId },
|
||||||
langDateTime(QDateTime::currentDateTime()),
|
langDateTime(QDateTime::currentDateTime()),
|
||||||
|
@ -400,178 +457,119 @@ void FormController::saveFieldIdentity(
|
||||||
Expects(index >= 0 && index < _form.fields.size());
|
Expects(index >= 0 && index < _form.fields.size());
|
||||||
Expects(_form.fields[index].type == Field::Type::Identity);
|
Expects(_form.fields[index].type == Field::Type::Identity);
|
||||||
|
|
||||||
_form.fields[index].data.values[qsl("first_name")] = data.name;
|
_form.fields[index].parsedData[qsl("first_name")] = data.name;
|
||||||
_form.fields[index].data.values[qsl("last_name")] = data.surname;
|
_form.fields[index].parsedData[qsl("last_name")] = data.surname;
|
||||||
|
|
||||||
saveData(index);
|
saveIdentity(index);
|
||||||
saveFiles(index);
|
|
||||||
|
|
||||||
_editBox->closeBox();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<QString, QString> FormController::fillData(
|
void FormController::saveIdentity(int index) {
|
||||||
const Value &from) const {
|
|
||||||
if (from.dataEncrypted.isEmpty()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
const auto valueHash = bytes::make_span(from.dataHash);
|
|
||||||
const auto valueSecret = DecryptValueSecret(
|
|
||||||
bytes::make_span(from.dataSecretEncrypted),
|
|
||||||
_secret,
|
|
||||||
valueHash);
|
|
||||||
return DeserializeData(DecryptData(
|
|
||||||
bytes::make_span(from.dataEncrypted),
|
|
||||||
valueHash,
|
|
||||||
valueSecret));
|
|
||||||
}
|
|
||||||
|
|
||||||
void FormController::saveData(int index) {
|
|
||||||
Expects(index >= 0 && index < _form.fields.size());
|
Expects(index >= 0 && index < _form.fields.size());
|
||||||
|
Expects(_form.fields[index].type == Field::Type::Identity);
|
||||||
|
|
||||||
if (_secret.empty()) {
|
if (_secret.empty()) {
|
||||||
generateSecret([=] {
|
_secretCallbacks.push_back([=] {
|
||||||
saveData(index);
|
saveIdentity(index);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto &data = _form.fields[index].data;
|
|
||||||
const auto encrypted = EncryptData(SerializeData(data.values));
|
|
||||||
|
|
||||||
// #TODO file_hash + file_hash + ...
|
_editBox->closeBox();
|
||||||
// PrepareValueHash(encrypted.hash, valueSecret);
|
|
||||||
const auto valueHash = encrypted.hash;
|
auto &field = _form.fields[index];
|
||||||
auto valueHashString = QString();
|
|
||||||
valueHashString.reserve(valueHash.size() * 2);
|
auto inputFiles = QVector<MTPInputSecureFile>();
|
||||||
const auto hex = [](uchar value) -> QChar {
|
inputFiles.reserve(field.filesInEdit.size());
|
||||||
return (value >= 10) ? ('a' + (value - 10)) : ('0' + value);
|
for (const auto &file : field.filesInEdit) {
|
||||||
};
|
if (const auto uploaded = file.uploaded.get()) {
|
||||||
for (const auto byte : valueHash) {
|
inputFiles.push_back(MTP_inputSecureFileUploaded(
|
||||||
const auto value = uchar(byte);
|
MTP_long(file.fields.id),
|
||||||
const auto high = uchar(value / 16);
|
MTP_int(uploaded->partsCount),
|
||||||
const auto low = uchar(value % 16);
|
MTP_bytes(uploaded->md5checksum),
|
||||||
valueHashString.append(hex(high)).append(hex(low));
|
MTP_bytes(file.fields.fileHash)));
|
||||||
|
} else {
|
||||||
|
inputFiles.push_back(MTP_inputSecureFile(
|
||||||
|
MTP_long(file.fields.id),
|
||||||
|
MTP_long(file.fields.accessHash)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (field.secret.empty()) {
|
||||||
|
field.secret = GenerateSecretBytes();
|
||||||
|
}
|
||||||
|
const auto encryptedData = EncryptData(
|
||||||
|
SerializeData(field.parsedData),
|
||||||
|
field.secret);
|
||||||
|
const auto fileHashes = ranges::view::all(
|
||||||
|
field.filesInEdit
|
||||||
|
) | ranges::view::transform([](const EditFile &file) {
|
||||||
|
return bytes::make_span(file.fields.fileHash);
|
||||||
|
});
|
||||||
|
const auto valueHash = openssl::Sha256(bytes::concatenate(
|
||||||
|
encryptedData.hash,
|
||||||
|
bytes::concatenate(fileHashes),
|
||||||
|
field.secret));
|
||||||
|
|
||||||
const auto encryptedValueSecret = EncryptValueSecret(
|
field.encryptedSecret = EncryptValueSecret(
|
||||||
encrypted.secret,
|
field.secret,
|
||||||
_secret,
|
_secret,
|
||||||
valueHash);
|
valueHash);
|
||||||
request(MTPaccount_SaveSecureValue(MTP_inputSecureValueData(
|
request(MTPaccount_SaveSecureValue(
|
||||||
MTP_string(data.name),
|
MTP_inputSecureValueIdentity(
|
||||||
MTP_bytes(encrypted.bytes),
|
MTP_secureData(
|
||||||
MTP_string(valueHashString),
|
MTP_bytes(encryptedData.bytes),
|
||||||
MTP_bytes(encryptedValueSecret)
|
MTP_bytes(encryptedData.hash)),
|
||||||
))).done([=](const MTPaccount_SecureValueResult &result) {
|
MTP_vector<MTPInputSecureFile>(inputFiles),
|
||||||
if (result.type() == mtpc_account_secureValueResultSaved) {
|
MTP_bytes(field.encryptedSecret),
|
||||||
Ui::show(Box<InformBox>("Saved"), LayerOption::KeepOther);
|
MTP_bytes(valueHash)),
|
||||||
} else if (result.type() == mtpc_account_secureValueVerificationNeeded) {
|
MTP_long(CountSecureSecretHash(_secret))
|
||||||
Ui::show(Box<InformBox>("Verification needed :("), LayerOption::KeepOther);
|
)).done([=](const MTPSecureValueSaved &result) {
|
||||||
}
|
Ui::show(Box<InformBox>("Saved"), LayerOption::KeepOther);
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
Ui::show(Box<InformBox>("Error saving value."));
|
Ui::show(Box<InformBox>("Error saving value."));
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::saveFiles(int index) {
|
void FormController::generateSecret(bytes::const_span password) {
|
||||||
Expects(index >= 0 && index < _form.fields.size());
|
|
||||||
Expects(_form.fields[index].document.has_value());
|
|
||||||
|
|
||||||
if (_secret.empty()) {
|
|
||||||
generateSecret([=] {
|
|
||||||
saveFiles(index);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto &document = *_form.fields[index].document;
|
|
||||||
if (document.filesSecret.empty()) {
|
|
||||||
Assert(document.filesInEdit.empty());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto filesHash = computeFilesHash(
|
|
||||||
ranges::view::all(
|
|
||||||
document.filesInEdit
|
|
||||||
) | ranges::view::transform([=](const EditFile &file) {
|
|
||||||
return bytes::make_span(file.fields.fileHash);
|
|
||||||
}),
|
|
||||||
document.filesSecret);
|
|
||||||
|
|
||||||
auto filesHashString = QString();
|
|
||||||
filesHashString.reserve(filesHash.size() * 2);
|
|
||||||
const auto hex = [](uchar value) -> QChar {
|
|
||||||
return (value >= 10) ? ('a' + (value - 10)) : ('0' + value);
|
|
||||||
};
|
|
||||||
for (const auto byte : filesHash) {
|
|
||||||
const auto value = uchar(byte);
|
|
||||||
const auto high = uchar(value / 16);
|
|
||||||
const auto low = uchar(value % 16);
|
|
||||||
filesHashString.append(hex(high)).append(hex(low));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto files = QVector<MTPInputSecureFile>();
|
|
||||||
files.reserve(document.filesInEdit.size());
|
|
||||||
for (const auto &file : document.filesInEdit) {
|
|
||||||
if (const auto uploaded = file.uploaded.get()) {
|
|
||||||
auto fileHashString = QString();
|
|
||||||
for (const auto byte : file.fields.fileHash) {
|
|
||||||
const auto value = uchar(byte);
|
|
||||||
const auto high = uchar(value / 16);
|
|
||||||
const auto low = uchar(value % 16);
|
|
||||||
fileHashString.append(hex(high)).append(hex(low));
|
|
||||||
}
|
|
||||||
files.push_back(MTP_inputSecureFileUploaded(
|
|
||||||
MTP_long(file.fields.id),
|
|
||||||
MTP_int(uploaded->partsCount),
|
|
||||||
MTP_bytes(uploaded->md5checksum),
|
|
||||||
MTP_string(fileHashString)));
|
|
||||||
} else {
|
|
||||||
files.push_back(MTP_inputSecureFile(
|
|
||||||
MTP_long(file.fields.id),
|
|
||||||
MTP_long(file.fields.accessHash)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto encryptedFilesSecret = EncryptValueSecret(
|
|
||||||
document.filesSecret,
|
|
||||||
_secret,
|
|
||||||
filesHash);
|
|
||||||
request(MTPaccount_SaveSecureValue(MTP_inputSecureValueFile(
|
|
||||||
MTP_string(document.name),
|
|
||||||
MTP_vector<MTPInputSecureFile>(files),
|
|
||||||
MTP_string(filesHashString),
|
|
||||||
MTP_bytes(encryptedFilesSecret)
|
|
||||||
))).done([=](const MTPaccount_SecureValueResult &result) {
|
|
||||||
if (result.type() == mtpc_account_secureValueResultSaved) {
|
|
||||||
Ui::show(Box<InformBox>("Files Saved"), LayerOption::KeepOther);
|
|
||||||
} else if (result.type() == mtpc_account_secureValueVerificationNeeded) {
|
|
||||||
Ui::show(Box<InformBox>("Verification needed :("), LayerOption::KeepOther);
|
|
||||||
}
|
|
||||||
}).fail([=](const RPCError &error) {
|
|
||||||
Ui::show(Box<InformBox>("Error saving files."));
|
|
||||||
}).send();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FormController::generateSecret(base::lambda<void()> callback) {
|
|
||||||
_secretCallbacks.push_back(callback);
|
|
||||||
if (_saveSecretRequestId) {
|
if (_saveSecretRequestId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto secret = GenerateSecretBytes();
|
auto secret = GenerateSecretBytes();
|
||||||
|
|
||||||
|
auto randomSaltPart = bytes::vector(8);
|
||||||
|
bytes::set_random(randomSaltPart);
|
||||||
|
auto newSecureSaltFull = bytes::concatenate(
|
||||||
|
_password.newSecureSalt,
|
||||||
|
randomSaltPart);
|
||||||
|
|
||||||
|
const auto hashForSecret = openssl::Sha512(bytes::concatenate(
|
||||||
|
newSecureSaltFull,
|
||||||
|
password,
|
||||||
|
newSecureSaltFull));
|
||||||
|
const auto hashForAuth = openssl::Sha256(bytes::concatenate(
|
||||||
|
_password.salt,
|
||||||
|
password,
|
||||||
|
_password.salt));
|
||||||
|
|
||||||
|
auto secureSecretHash = CountSecureSecretHash(secret);
|
||||||
auto encryptedSecret = EncryptSecretBytes(
|
auto encryptedSecret = EncryptSecretBytes(
|
||||||
secret,
|
secret,
|
||||||
_passwordHashForSecret);
|
hashForSecret);
|
||||||
using Flag = MTPDaccount_passwordInputSettings::Flag;
|
using Flag = MTPDaccount_passwordInputSettings::Flag;
|
||||||
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
|
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
|
||||||
MTP_bytes(_passwordHashForAuth),
|
MTP_bytes(hashForAuth),
|
||||||
MTP_account_passwordInputSettings(
|
MTP_account_passwordInputSettings(
|
||||||
MTP_flags(Flag::f_new_secure_secret),
|
MTP_flags(Flag::f_new_secure_secret),
|
||||||
MTPbytes(), // new_salt
|
MTPbytes(), // new_salt
|
||||||
MTPbytes(), // new_password_hash
|
MTPbytes(), // new_password_hash
|
||||||
MTPstring(), // hint
|
MTPstring(), // hint
|
||||||
MTPstring(), // email
|
MTPstring(), // email
|
||||||
MTP_bytes(encryptedSecret))
|
MTP_bytes(newSecureSaltFull),
|
||||||
|
MTP_bytes(encryptedSecret),
|
||||||
|
MTP_long(secureSecretHash))
|
||||||
)).done([=](const MTPBool &result) {
|
)).done([=](const MTPBool &result) {
|
||||||
_saveSecretRequestId = 0;
|
_saveSecretRequestId = 0;
|
||||||
_secret = secret;
|
_secret = secret;
|
||||||
|
//_password.salt = newPasswordSaltFull;
|
||||||
for (const auto &callback : base::take(_secretCallbacks)) {
|
for (const auto &callback : base::take(_secretCallbacks)) {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
@ -583,23 +581,12 @@ void FormController::generateSecret(base::lambda<void()> callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::requestForm() {
|
void FormController::requestForm() {
|
||||||
auto scope = QVector<MTPstring>();
|
|
||||||
scope.reserve(_request.scope.size());
|
|
||||||
for (const auto &element : _request.scope) {
|
|
||||||
scope.push_back(MTP_string(element));
|
|
||||||
}
|
|
||||||
auto normalizedKey = _request.publicKey;
|
auto normalizedKey = _request.publicKey;
|
||||||
normalizedKey.replace("\r\n", "\n");
|
normalizedKey.replace("\r\n", "\n");
|
||||||
const auto bytes = normalizedKey.toUtf8();
|
|
||||||
_formRequestId = request(MTPaccount_GetAuthorizationForm(
|
_formRequestId = request(MTPaccount_GetAuthorizationForm(
|
||||||
MTP_flags(MTPaccount_GetAuthorizationForm::Flag::f_origin
|
|
||||||
| MTPaccount_GetAuthorizationForm::Flag::f_public_key),
|
|
||||||
MTP_int(_request.botId),
|
MTP_int(_request.botId),
|
||||||
MTP_vector<MTPstring>(std::move(scope)),
|
MTP_string(_request.scope),
|
||||||
MTP_string(_origin),
|
MTP_bytes(normalizedKey.toUtf8())
|
||||||
MTPstring(), // package_name
|
|
||||||
MTPstring(), // bundle_id
|
|
||||||
MTP_bytes(bytes)
|
|
||||||
)).done([=](const MTPaccount_AuthorizationForm &result) {
|
)).done([=](const MTPaccount_AuthorizationForm &result) {
|
||||||
_formRequestId = 0;
|
_formRequestId = 0;
|
||||||
formDone(result);
|
formDone(result);
|
||||||
|
@ -608,130 +595,110 @@ void FormController::requestForm() {
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto FormController::convertValue(
|
template <typename DataType>
|
||||||
const MTPSecureValue &value) const -> Value {
|
auto FormController::parseEncryptedField(
|
||||||
auto result = Value();
|
Field::Type type,
|
||||||
switch (value.type()) {
|
const DataType &data) const -> Field {
|
||||||
case mtpc_secureValueEmpty: {
|
Expects(data.vdata.type() == mtpc_secureData);
|
||||||
const auto &data = value.c_secureValueEmpty();
|
|
||||||
result.name = qs(data.vname);
|
auto result = Field(type);
|
||||||
} break;
|
if (data.has_verified()) {
|
||||||
case mtpc_secureValueData: {
|
result.verification = parseVerified(data.vverified);
|
||||||
const auto &data = value.c_secureValueData();
|
}
|
||||||
result.name = qs(data.vname);
|
result.encryptedSecret = bytes::make_vector(data.vsecret.v);
|
||||||
result.dataEncrypted = data.vdata.v;
|
result.hash = bytes::make_vector(data.vhash.v);
|
||||||
const auto hashString = qs(data.vhash);
|
const auto &fields = data.vdata.c_secureData();
|
||||||
for (auto i = 0, count = hashString.size(); i + 1 < count; i += 2) {
|
result.originalData = fields.vdata.v;
|
||||||
auto digit = [&](QChar ch) -> int {
|
result.dataHash = bytes::make_vector(fields.vdata_hash.v);
|
||||||
const auto code = ch.unicode();
|
for (const auto &file : data.vfiles.v) {
|
||||||
if (code >= 'a' && code <= 'f') {
|
switch (file.type()) {
|
||||||
return (code - 'a') + 10;
|
case mtpc_secureFileEmpty: {
|
||||||
} else if (code >= 'A' && code <= 'F') {
|
result.files.push_back(File());
|
||||||
return (code - 'A') + 10;
|
} break;
|
||||||
} else if (code >= '0' && code <= '9') {
|
case mtpc_secureFile: {
|
||||||
return (code - '0');
|
const auto &fields = file.c_secureFile();
|
||||||
}
|
auto normal = File();
|
||||||
return -1;
|
normal.id = fields.vid.v;
|
||||||
};
|
normal.accessHash = fields.vaccess_hash.v;
|
||||||
const auto ch1 = digit(hashString[i]);
|
normal.size = fields.vsize.v;
|
||||||
const auto ch2 = digit(hashString[i + 1]);
|
normal.dcId = fields.vdc_id.v;
|
||||||
if (ch1 >= 0 && ch2 >= 0) {
|
normal.fileHash = bytes::make_vector(fields.vfile_hash.v);
|
||||||
const auto byte = ch1 * 16 + ch2;
|
result.files.push_back(std::move(normal));
|
||||||
result.dataHash.push_back(byte);
|
} break;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (result.dataHash.size() != 32) {
|
|
||||||
result.dataHash.clear();
|
|
||||||
}
|
|
||||||
// result.dataHash = data.vhash.v;
|
|
||||||
result.dataSecretEncrypted = data.vsecret.v;
|
|
||||||
} break;
|
|
||||||
case mtpc_secureValueText: {
|
|
||||||
const auto &data = value.c_secureValueText();
|
|
||||||
result.name = qs(data.vname);
|
|
||||||
result.text = qs(data.vtext);
|
|
||||||
result.textHash = data.vhash.v;
|
|
||||||
} break;
|
|
||||||
case mtpc_secureValueFile: {
|
|
||||||
const auto &data = value.c_secureValueFile();
|
|
||||||
result.name = qs(data.vname);
|
|
||||||
const auto hashString = qs(data.vhash);
|
|
||||||
for (auto i = 0, count = hashString.size(); i + 1 < count; i += 2) {
|
|
||||||
auto digit = [&](QChar ch) -> int {
|
|
||||||
const auto code = ch.unicode();
|
|
||||||
if (code >= 'a' && code <= 'f') {
|
|
||||||
return (code - 'a') + 10;
|
|
||||||
} else if (code >= 'A' && code <= 'F') {
|
|
||||||
return (code - 'A') + 10;
|
|
||||||
} else if (code >= '0' && code <= '9') {
|
|
||||||
return (code - '0');
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
};
|
|
||||||
const auto ch1 = digit(hashString[i]);
|
|
||||||
const auto ch2 = digit(hashString[i + 1]);
|
|
||||||
if (ch1 >= 0 && ch2 >= 0) {
|
|
||||||
const auto byte = ch1 * 16 + ch2;
|
|
||||||
result.filesHash.push_back(byte);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (result.filesHash.size() != 32) {
|
|
||||||
result.filesHash.clear();
|
|
||||||
}
|
|
||||||
// result.filesHash = data.vhash.v;
|
|
||||||
result.filesSecretEncrypted = data.vsecret.v;
|
|
||||||
for (const auto &file : data.vfile.v) {
|
|
||||||
switch (file.type()) {
|
|
||||||
case mtpc_secureFileEmpty: {
|
|
||||||
result.files.push_back(File());
|
|
||||||
} break;
|
|
||||||
case mtpc_secureFile: {
|
|
||||||
const auto &fields = file.c_secureFile();
|
|
||||||
auto normal = File();
|
|
||||||
normal.id = fields.vid.v;
|
|
||||||
normal.accessHash = fields.vaccess_hash.v;
|
|
||||||
normal.size = fields.vsize.v;
|
|
||||||
normal.dcId = fields.vdc_id.v;
|
|
||||||
const auto fileHashString = qs(fields.vfile_hash);
|
|
||||||
for (auto i = 0, count = fileHashString.size(); i + 1 < count; i += 2) {
|
|
||||||
auto digit = [&](QChar ch) -> int {
|
|
||||||
const auto code = ch.unicode();
|
|
||||||
if (code >= 'a' && code <= 'f') {
|
|
||||||
return (code - 'a') + 10;
|
|
||||||
} else if (code >= 'A' && code <= 'F') {
|
|
||||||
return (code - 'A') + 10;
|
|
||||||
} else if (code >= '0' && code <= '9') {
|
|
||||||
return (code - '0');
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
};
|
|
||||||
const auto ch1 = digit(fileHashString[i]);
|
|
||||||
const auto ch2 = digit(fileHashString[i + 1]);
|
|
||||||
if (ch1 >= 0 && ch2 >= 0) {
|
|
||||||
const auto byte = ch1 * 16 + ch2;
|
|
||||||
normal.fileHash.push_back(gsl::byte(byte));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (normal.fileHash.size() != 32) {
|
|
||||||
normal.fileHash.clear();
|
|
||||||
}
|
|
||||||
// normal.fileHash = bytes::make_vector(fields.vfile_hash.v);
|
|
||||||
result.files.push_back(std::move(normal));
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename DataType>
|
||||||
|
auto FormController::parsePlainTextField(
|
||||||
|
Field::Type type,
|
||||||
|
const QByteArray &value,
|
||||||
|
const DataType &data) const -> Field {
|
||||||
|
auto result = Field(type);
|
||||||
|
const auto check = bytes::compare(
|
||||||
|
bytes::make_span(data.vhash.v),
|
||||||
|
openssl::Sha256(bytes::make_span(value)));
|
||||||
|
if (check != 0) {
|
||||||
|
LOG(("API Error: Bad hash for plain text value. "
|
||||||
|
"Value '%1', hash '%2'"
|
||||||
|
).arg(QString::fromUtf8(value)
|
||||||
|
).arg(Logs::mb(data.vhash.v.data(), data.vhash.v.size()).str()
|
||||||
|
));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
result.parsedData[QString("value")] = QString::fromUtf8(value);
|
||||||
|
if (data.has_verified()) {
|
||||||
|
result.verification = parseVerified(data.vverified);
|
||||||
|
}
|
||||||
|
result.hash = bytes::make_vector(data.vhash.v);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto FormController::parseValue(
|
||||||
|
const MTPSecureValue &value) const -> Field {
|
||||||
|
switch (value.type()) {
|
||||||
|
case mtpc_secureValueIdentity: {
|
||||||
|
return parseEncryptedField(
|
||||||
|
Field::Type::Identity,
|
||||||
|
value.c_secureValueIdentity());
|
||||||
|
} break;
|
||||||
|
case mtpc_secureValueAddress: {
|
||||||
|
return parseEncryptedField(
|
||||||
|
Field::Type::Address,
|
||||||
|
value.c_secureValueAddress());
|
||||||
|
} break;
|
||||||
|
case mtpc_secureValuePhone: {
|
||||||
|
const auto &data = value.c_secureValuePhone();
|
||||||
|
return parsePlainTextField(
|
||||||
|
Field::Type::Phone,
|
||||||
|
data.vphone.v,
|
||||||
|
data);
|
||||||
|
} break;
|
||||||
|
case mtpc_secureValueEmail: {
|
||||||
|
const auto &data = value.c_secureValueEmail();
|
||||||
|
return parsePlainTextField(
|
||||||
|
Field::Type::Phone,
|
||||||
|
data.vemail.v,
|
||||||
|
data);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
Unexpected("secureValue type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto FormController::parseVerified(const MTPSecureValueVerified &data) const
|
||||||
|
-> Verification {
|
||||||
|
Expects(data.type() == mtpc_secureValueVerified);
|
||||||
|
|
||||||
|
const auto &fields = data.c_secureValueVerified();
|
||||||
|
return Verification{ fields.vdate.v, qs(fields.vprovider) };
|
||||||
|
}
|
||||||
|
|
||||||
auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* {
|
auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* {
|
||||||
for (auto &field : _form.fields) {
|
for (auto &field : _form.fields) {
|
||||||
if (auto &document = field.document) {
|
for (auto &file : field.filesInEdit) {
|
||||||
for (auto &file : document->filesInEdit) {
|
if (file.uploaded && file.uploaded->fullId == fullId) {
|
||||||
if (file.uploaded && file.uploaded->fullId == fullId) {
|
return &file;
|
||||||
return &file;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -741,11 +708,9 @@ auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* {
|
||||||
auto FormController::findFile(const FileKey &key)
|
auto FormController::findFile(const FileKey &key)
|
||||||
-> std::pair<Field*, File*> {
|
-> std::pair<Field*, File*> {
|
||||||
for (auto &field : _form.fields) {
|
for (auto &field : _form.fields) {
|
||||||
if (auto &document = field.document) {
|
for (auto &file : field.files) {
|
||||||
for (auto &file : document->files) {
|
if (file.dcId == key.dcId && file.id == key.id) {
|
||||||
if (file.dcId == key.dcId && file.id == key.id) {
|
return { &field, &file };
|
||||||
return { &field, &file };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -763,33 +728,40 @@ void FormController::parseForm(const MTPaccount_AuthorizationForm &result) {
|
||||||
Expects(result.type() == mtpc_account_authorizationForm);
|
Expects(result.type() == mtpc_account_authorizationForm);
|
||||||
|
|
||||||
const auto &data = result.c_account_authorizationForm();
|
const auto &data = result.c_account_authorizationForm();
|
||||||
_form.requestWrite = data.is_request_write();
|
|
||||||
if (_request.botId != data.vbot_id.v) {
|
|
||||||
LOG(("API Error: Wrong account.authorizationForm.bot_id."));
|
|
||||||
_request.botId = data.vbot_id.v;
|
|
||||||
}
|
|
||||||
App::feedUsers(data.vusers);
|
|
||||||
for (const auto &field : data.vfields.v) {
|
|
||||||
Assert(field.type() == mtpc_authField);
|
|
||||||
|
|
||||||
|
auto values = std::vector<Field>();
|
||||||
|
for (const auto &value : data.vvalues.v) {
|
||||||
|
values.push_back(parseValue(value));
|
||||||
|
}
|
||||||
|
const auto findValue = [&](Field::Type type) -> Field* {
|
||||||
|
for (auto &value : values) {
|
||||||
|
if (value.type == type) {
|
||||||
|
return &value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
_form.requestWrite = false;
|
||||||
|
App::feedUsers(data.vusers);
|
||||||
|
for (const auto &required : data.vrequired_types.v) {
|
||||||
using Type = Field::Type;
|
using Type = Field::Type;
|
||||||
|
|
||||||
const auto &data = field.c_authField();
|
|
||||||
const auto type = [&] {
|
const auto type = [&] {
|
||||||
switch (data.vtype.type()) {
|
switch (required.type()) {
|
||||||
case mtpc_authFieldTypeIdentity: return Type::Identity;
|
case mtpc_secureValueTypeIdentity: return Type::Identity;
|
||||||
case mtpc_authFieldTypeAddress: return Type::Address;
|
case mtpc_secureValueTypeAddress: return Type::Address;
|
||||||
case mtpc_authFieldTypeEmail: return Type::Email;
|
case mtpc_secureValueTypeEmail: return Type::Email;
|
||||||
case mtpc_authFieldTypePhone: return Type::Phone;
|
case mtpc_secureValueTypePhone: return Type::Phone;
|
||||||
}
|
}
|
||||||
Unexpected("Type in authField.type.");
|
Unexpected("Type in secureValueType type.");
|
||||||
}();
|
}();
|
||||||
auto entry = Field(type);
|
|
||||||
entry.data = convertValue(data.vdata);
|
if (auto field = findValue(type)) {
|
||||||
if (data.has_document()) {
|
_form.fields.push_back(std::move(*field));
|
||||||
entry.document = convertValue(data.vdocument);
|
} else {
|
||||||
|
_form.fields.push_back(Field(type));
|
||||||
}
|
}
|
||||||
_form.fields.push_back(std::move(entry));
|
|
||||||
}
|
}
|
||||||
_bot = App::userLoaded(_request.botId);
|
_bot = App::userLoaded(_request.botId);
|
||||||
}
|
}
|
||||||
|
@ -837,17 +809,19 @@ void FormController::passwordFail(const RPCError &error) {
|
||||||
|
|
||||||
void FormController::parsePassword(const MTPDaccount_noPassword &result) {
|
void FormController::parsePassword(const MTPDaccount_noPassword &result) {
|
||||||
_password.unconfirmedPattern = qs(result.vemail_unconfirmed_pattern);
|
_password.unconfirmedPattern = qs(result.vemail_unconfirmed_pattern);
|
||||||
_password.newSalt = result.vnew_salt.v;
|
_password.newSalt = bytes::make_vector(result.vnew_salt.v);
|
||||||
openssl::AddRandomSeed(bytes::make_span(result.vsecret_random.v));
|
_password.newSecureSalt = bytes::make_vector(result.vnew_secure_salt.v);
|
||||||
|
openssl::AddRandomSeed(bytes::make_span(result.vsecure_random.v));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::parsePassword(const MTPDaccount_password &result) {
|
void FormController::parsePassword(const MTPDaccount_password &result) {
|
||||||
_password.hint = qs(result.vhint);
|
_password.hint = qs(result.vhint);
|
||||||
_password.hasRecovery = mtpIsTrue(result.vhas_recovery);
|
_password.hasRecovery = mtpIsTrue(result.vhas_recovery);
|
||||||
_password.salt = result.vcurrent_salt.v;
|
_password.salt = bytes::make_vector(result.vcurrent_salt.v);
|
||||||
_password.unconfirmedPattern = qs(result.vemail_unconfirmed_pattern);
|
_password.unconfirmedPattern = qs(result.vemail_unconfirmed_pattern);
|
||||||
_password.newSalt = result.vnew_salt.v;
|
_password.newSalt = bytes::make_vector(result.vnew_salt.v);
|
||||||
openssl::AddRandomSeed(bytes::make_span(result.vsecret_random.v));
|
_password.newSecureSalt = bytes::make_vector(result.vnew_secure_salt.v);
|
||||||
|
openssl::AddRandomSeed(bytes::make_span(result.vsecure_random.v));
|
||||||
}
|
}
|
||||||
|
|
||||||
FormController::~FormController() = default;
|
FormController::~FormController() = default;
|
||||||
|
|
|
@ -25,12 +25,12 @@ namespace Passport {
|
||||||
struct FormRequest {
|
struct FormRequest {
|
||||||
FormRequest(
|
FormRequest(
|
||||||
UserId botId,
|
UserId botId,
|
||||||
const QStringList &scope,
|
const QString &scope,
|
||||||
const QString &callbackUrl,
|
const QString &callbackUrl,
|
||||||
const QString &publicKey);
|
const QString &publicKey);
|
||||||
|
|
||||||
UserId botId;
|
UserId botId;
|
||||||
QStringList scope;
|
QString scope;
|
||||||
QString callbackUrl;
|
QString callbackUrl;
|
||||||
QString publicKey;
|
QString publicKey;
|
||||||
|
|
||||||
|
@ -128,23 +128,9 @@ private:
|
||||||
File fields;
|
File fields;
|
||||||
std::unique_ptr<UploadedScan> uploaded;
|
std::unique_ptr<UploadedScan> uploaded;
|
||||||
};
|
};
|
||||||
struct Value {
|
struct Verification {
|
||||||
QString name;
|
TimeId date;
|
||||||
|
QString provider;
|
||||||
QByteArray dataEncrypted;
|
|
||||||
QByteArray dataHash;
|
|
||||||
QByteArray dataSecretEncrypted;
|
|
||||||
std::map<QString, QString> values;
|
|
||||||
|
|
||||||
QString text;
|
|
||||||
QByteArray textHash;
|
|
||||||
|
|
||||||
std::vector<File> files;
|
|
||||||
QByteArray filesHash;
|
|
||||||
QByteArray filesSecretEncrypted;
|
|
||||||
bytes::vector filesSecret;
|
|
||||||
|
|
||||||
std::vector<EditFile> filesInEdit;
|
|
||||||
};
|
};
|
||||||
struct Field {
|
struct Field {
|
||||||
enum class Type {
|
enum class Type {
|
||||||
|
@ -153,25 +139,36 @@ private:
|
||||||
Phone,
|
Phone,
|
||||||
Email,
|
Email,
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit Field(Type type);
|
explicit Field(Type type);
|
||||||
Field(Field &&other) = default;
|
Field(Field &&other) = default;
|
||||||
|
Field &operator=(Field &&other) = default;
|
||||||
|
|
||||||
Type type;
|
Type type;
|
||||||
Value data;
|
QByteArray originalData;
|
||||||
base::optional<Value> document;
|
std::map<QString, QString> parsedData;
|
||||||
|
bytes::vector dataHash;
|
||||||
|
std::vector<File> files;
|
||||||
|
std::vector<EditFile> filesInEdit;
|
||||||
|
bytes::vector secret;
|
||||||
|
bytes::vector encryptedSecret;
|
||||||
|
bytes::vector hash;
|
||||||
|
base::optional<Verification> verification;
|
||||||
};
|
};
|
||||||
struct Form {
|
struct Form {
|
||||||
bool requestWrite = false;
|
bool requestWrite = false;
|
||||||
std::vector<Field> fields;
|
std::vector<Field> fields;
|
||||||
};
|
};
|
||||||
struct PasswordSettings {
|
struct PasswordSettings {
|
||||||
QByteArray salt;
|
bytes::vector salt;
|
||||||
QByteArray newSalt;
|
bytes::vector newSalt;
|
||||||
|
bytes::vector newSecureSalt;
|
||||||
QString hint;
|
QString hint;
|
||||||
QString unconfirmedPattern;
|
QString unconfirmedPattern;
|
||||||
|
QString confirmedEmail;
|
||||||
bool hasRecovery = false;
|
bool hasRecovery = false;
|
||||||
};
|
};
|
||||||
Value convertValue(const MTPSecureValue &value) const;
|
|
||||||
EditFile *findEditFile(const FullMsgId &fullId);
|
EditFile *findEditFile(const FullMsgId &fullId);
|
||||||
std::pair<Field*, File*> findFile(const FileKey &key);
|
std::pair<Field*, File*> findFile(const FileKey &key);
|
||||||
|
|
||||||
|
@ -182,21 +179,39 @@ 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;
|
||||||
|
template <typename DataType>
|
||||||
|
Field parseEncryptedField(
|
||||||
|
Field::Type type,
|
||||||
|
const DataType &data) const;
|
||||||
|
template <typename DataType>
|
||||||
|
Field parsePlainTextField(
|
||||||
|
Field::Type type,
|
||||||
|
const QByteArray &value,
|
||||||
|
const DataType &data) const;
|
||||||
|
Verification parseVerified(const MTPSecureValueVerified &data) const;
|
||||||
|
|
||||||
void passwordDone(const MTPaccount_Password &result);
|
void passwordDone(const MTPaccount_Password &result);
|
||||||
void passwordFail(const RPCError &error);
|
void passwordFail(const RPCError &error);
|
||||||
void parsePassword(const MTPDaccount_noPassword &settings);
|
void parsePassword(const MTPDaccount_noPassword &settings);
|
||||||
void parsePassword(const MTPDaccount_password &settings);
|
void parsePassword(const MTPDaccount_password &settings);
|
||||||
|
bytes::vector passwordHashForAuth(bytes::const_span password) const;
|
||||||
|
void validateSecureSecret(
|
||||||
|
bytes::const_span salt,
|
||||||
|
bytes::const_span encryptedSecret,
|
||||||
|
bytes::const_span password);
|
||||||
|
void decryptFields();
|
||||||
|
void decryptField(Field &field);
|
||||||
|
bool validateFieldSecret(Field &field);
|
||||||
|
void resetField(Field &field);
|
||||||
|
|
||||||
IdentityData fieldDataIdentity(const Field &field) const;
|
IdentityData fieldDataIdentity(const Field &field) const;
|
||||||
std::vector<ScanInfo> fieldFilesIdentity(const Field &field) const;
|
std::vector<ScanInfo> fieldFilesIdentity(const Field &field) const;
|
||||||
|
void saveIdentity(int index);
|
||||||
|
|
||||||
void loadFiles(const std::vector<File> &files);
|
void loadFiles(const std::vector<File> &files);
|
||||||
void fileLoaded(FileKey key, const QByteArray &bytes);
|
void fileLoaded(FileKey key, const QByteArray &bytes);
|
||||||
std::map<QString, QString> fillData(const Value &from) const;
|
void generateSecret(bytes::const_span password);
|
||||||
void saveData(int index);
|
|
||||||
void saveFiles(int index);
|
|
||||||
void generateSecret(base::lambda<void()> callback);
|
|
||||||
|
|
||||||
template <typename FileHashes>
|
template <typename FileHashes>
|
||||||
bytes::vector computeFilesHash(
|
bytes::vector computeFilesHash(
|
||||||
|
@ -210,7 +225,6 @@ private:
|
||||||
not_null<Window::Controller*> _controller;
|
not_null<Window::Controller*> _controller;
|
||||||
FormRequest _request;
|
FormRequest _request;
|
||||||
UserData *_bot = nullptr;
|
UserData *_bot = nullptr;
|
||||||
QString _origin;
|
|
||||||
|
|
||||||
mtpRequestId _formRequestId = 0;
|
mtpRequestId _formRequestId = 0;
|
||||||
mtpRequestId _passwordRequestId = 0;
|
mtpRequestId _passwordRequestId = 0;
|
||||||
|
@ -221,12 +235,9 @@ private:
|
||||||
std::map<FileKey, std::unique_ptr<mtpFileLoader>> _fileLoaders;
|
std::map<FileKey, std::unique_ptr<mtpFileLoader>> _fileLoaders;
|
||||||
rpl::event_stream<ScanInfo> _scanUpdated;
|
rpl::event_stream<ScanInfo> _scanUpdated;
|
||||||
|
|
||||||
bytes::vector _passwordHashForSecret;
|
|
||||||
bytes::vector _passwordHashForAuth;
|
|
||||||
bytes::vector _secret;
|
bytes::vector _secret;
|
||||||
std::vector<base::lambda<void()>> _secretCallbacks;
|
std::vector<base::lambda<void()>> _secretCallbacks;
|
||||||
mtpRequestId _saveSecretRequestId = 0;
|
mtpRequestId _saveSecretRequestId = 0;
|
||||||
QString _passwordEmail;
|
|
||||||
rpl::event_stream<> _secretReady;
|
rpl::event_stream<> _secretReady;
|
||||||
rpl::event_stream<QString> _passwordError;
|
rpl::event_stream<QString> _passwordError;
|
||||||
|
|
||||||
|
|
|
@ -85,12 +85,14 @@ void CloudPasswordState::onTurnOff() {
|
||||||
MTPaccount_UpdatePasswordSettings(
|
MTPaccount_UpdatePasswordSettings(
|
||||||
MTP_bytes(QByteArray()),
|
MTP_bytes(QByteArray()),
|
||||||
MTP_account_passwordInputSettings(
|
MTP_account_passwordInputSettings(
|
||||||
MTP_flags(MTPDaccount_passwordInputSettings::Flag::f_email),
|
MTP_flags(MTPDaccount_passwordInputSettings::Flag::f_email),
|
||||||
MTP_bytes(QByteArray()),
|
MTP_bytes(QByteArray()), // new_salt
|
||||||
MTP_bytes(QByteArray()),
|
MTP_bytes(QByteArray()), // new_password_hash
|
||||||
MTP_string(QString()),
|
MTP_string(QString()), // hint
|
||||||
MTP_string(QString()),
|
MTP_string(QString()), // email
|
||||||
MTP_bytes(QByteArray()))),
|
MTP_bytes(QByteArray()), // new_secure_salt
|
||||||
|
MTP_bytes(QByteArray()), // new_secure_secret
|
||||||
|
MTP_long(0))), // new_secure_secret_hash
|
||||||
rpcDone(&CloudPasswordState::offPasswordDone),
|
rpcDone(&CloudPasswordState::offPasswordDone),
|
||||||
rpcFail(&CloudPasswordState::offPasswordFail));
|
rpcFail(&CloudPasswordState::offPasswordFail));
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in New Issue