First version of IdentityBox with encrypted data.

This commit is contained in:
John Preston 2018-03-21 08:35:32 +04:00
parent 07e8a2bd85
commit f633ead3ab
10 changed files with 742 additions and 16 deletions

View File

@ -207,20 +207,40 @@ inline BigNum operator-(const BigNum &a, const BigNum &b) {
return result;
}
inline base::byte_array<SHA256_DIGEST_LENGTH> Sha256(base::const_byte_span bytes) {
auto result = base::byte_array<SHA256_DIGEST_LENGTH>();
SHA256(reinterpret_cast<const unsigned char*>(bytes.data()), bytes.size(), reinterpret_cast<unsigned char*>(result.data()));
inline base::byte_array<SHA512_DIGEST_LENGTH> Sha512(
base::const_byte_span bytes) {
auto result = base::byte_array<SHA512_DIGEST_LENGTH>();
SHA512(
reinterpret_cast<const unsigned char*>(bytes.data()),
bytes.size(),
reinterpret_cast<unsigned char*>(result.data()));
return result;
}
inline base::byte_array<SHA_DIGEST_LENGTH> Sha1(base::const_byte_span bytes) {
inline base::byte_array<SHA256_DIGEST_LENGTH> Sha256(
base::const_byte_span bytes) {
auto result = base::byte_array<SHA256_DIGEST_LENGTH>();
SHA256(
reinterpret_cast<const unsigned char*>(bytes.data()),
bytes.size(),
reinterpret_cast<unsigned char*>(result.data()));
return result;
}
inline base::byte_array<SHA_DIGEST_LENGTH> Sha1(
base::const_byte_span bytes) {
auto result = base::byte_array<SHA_DIGEST_LENGTH>();
SHA1(reinterpret_cast<const unsigned char*>(bytes.data()), bytes.size(), reinterpret_cast<unsigned char*>(result.data()));
SHA1(
reinterpret_cast<const unsigned char*>(bytes.data()),
bytes.size(),
reinterpret_cast<unsigned char*>(result.data()));
return result;
}
inline int FillRandom(base::byte_span bytes) {
return RAND_bytes(reinterpret_cast<unsigned char*>(bytes.data()), bytes.size());
return RAND_bytes(
reinterpret_cast<unsigned char*>(bytes.data()),
bytes.size());
}
inline void AddRandomSeed(base::const_byte_span bytes) {

View File

@ -494,6 +494,10 @@ int32 *hashSha256(const void *data, uint32 len, void *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
namespace {

View File

@ -0,0 +1,85 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "passport/passport_edit_identity_box.h"
#include "passport/passport_form_controller.h"
#include "ui/widgets/input_fields.h"
#include "lang/lang_keys.h"
#include "styles/style_widgets.h"
#include "styles/style_boxes.h"
#include "styles/style_passport.h"
namespace Passport {
IdentityBox::IdentityBox(
QWidget*,
not_null<FormController*> controller,
int fieldIndex,
const IdentityData &data)
: _controller(controller)
, _fieldIndex(fieldIndex)
, _name(
this,
st::defaultInputField,
langFactory(lng_signup_firstname),
data.name)
, _surname(
this,
st::defaultInputField,
langFactory(lng_signup_lastname),
data.surname) {
}
void IdentityBox::prepare() {
setTitle(langFactory(lng_passport_identity_title));
addButton(langFactory(lng_settings_save), [=] {
save();
});
addButton(langFactory(lng_cancel), [=] {
closeBox();
});
setDimensions(
st::boxWideWidth,
(st::contactPadding.top()
+ _name->height()
+ st::contactSkip
+ _surname->height()
+ st::contactPadding.bottom()
+ st::boxPadding.bottom()));
}
void IdentityBox::setInnerFocus() {
_name->setFocusFast();
}
void IdentityBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
_name->resize((width()
- st::boxPadding.left()
- st::boxPadding.right()),
_name->height());
_surname->resize(_name->width(), _surname->height());
_name->moveToLeft(
st::boxPadding.left(),
st::contactPadding.top());
_surname->moveToLeft(
st::boxPadding.left(),
_name->y() + _name->height() + st::contactSkip);
}
void IdentityBox::save() {
auto data = IdentityData();
data.name = _name->getLastText();
data.surname = _surname->getLastText();
_controller->saveFieldIdentity(_fieldIndex, data);
}
} // namespace Passport

View File

@ -0,0 +1,50 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "boxes/abstract_box.h"
namespace Ui {
class InputField;
} // namespace Ui
namespace Passport {
class FormController;
struct IdentityData {
QString name;
QString surname;
};
class IdentityBox : public BoxContent {
public:
IdentityBox(
QWidget*,
not_null<FormController*> controller,
int fieldIndex,
const IdentityData &data);
protected:
void prepare() override;
void setInnerFocus() override;
void resizeEvent(QResizeEvent *e) override;
private:
void save();
not_null<FormController*> _controller;
int _fieldIndex = -1;
object_ptr<Ui::InputField> _name;
object_ptr<Ui::InputField> _surname;
};
} // namespace Passport

View File

@ -0,0 +1,323 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "passport/passport_encryption.h"
#include "base/openssl_help.h"
#include <openssl/aes.h>
#include <openssl/crypto.h>
namespace Passport {
namespace {
constexpr auto kAesKeyLength = 32;
constexpr auto kAesIvLength = 16;
constexpr auto kSecretSize = 32;
constexpr auto kMinPadding = 32;
constexpr auto kMaxPadding = 255;
constexpr auto kAlignTo = 16;
base::byte_vector SerializeData(const std::map<QString, QString> &data) {
auto root = QJsonObject();
for (const auto &[key, value] : data) {
root.insert(key, value);
}
auto document = QJsonDocument(root);
const auto result = document.toJson(QJsonDocument::Compact);
const auto bytes = gsl::as_bytes(gsl::make_span(result));
return { bytes.begin(), bytes.end() };
}
std::map<QString, QString> DeserializeData(base::const_byte_span bytes) {
const auto serialized = QByteArray::fromRawData(
reinterpret_cast<const char*>(bytes.data()),
bytes.size());
auto error = QJsonParseError();
auto document = QJsonDocument::fromJson(serialized, &error);
if (error.error != QJsonParseError::NoError) {
LOG(("API Error: Could not deserialize decrypted JSON, error %1"
).arg(error.errorString()));
return {};
} else if (!document.isObject()) {
LOG(("API Error: decrypted JSON root is not an object."));
return {};
}
auto object = document.object();
auto result = std::map<QString, QString>();
for (auto i = object.constBegin(), e = object.constEnd(); i != e; ++i) {
const auto key = i.key();
switch (i->type()) {
case QJsonValue::Null: {
LOG(("API Error: null found inside decrypted JSON root. "
"Defaulting to empty string value."));
result[key] = QString();
} break;
case QJsonValue::Undefined: {
LOG(("API Error: undefined found inside decrypted JSON root. "
"Defaulting to empty string value."));
result[key] = QString();
} break;
case QJsonValue::Bool: {
LOG(("API Error: bool found inside decrypted JSON root. "
"Aborting."));
return {};
} break;
case QJsonValue::Double: {
LOG(("API Error: double found inside decrypted JSON root. "
"Converting to string."));
result[key] = QString::number(i->toDouble());
} break;
case QJsonValue::String: {
result[key] = i->toString();
} break;
case QJsonValue::Array: {
LOG(("API Error: array found inside decrypted JSON root. "
"Aborting."));
return {};
} break;
case QJsonValue::Object: {
LOG(("API Error: object found inside decrypted JSON root. "
"Aborting."));
return {};
} break;
}
}
return result;
}
} // namespace
struct AesParams {
base::byte_array<kAesKeyLength> key;
base::byte_array<kAesIvLength> iv;
};
AesParams PrepareAesParams(base::const_byte_span secretHash) {
const auto hash = openssl::Sha512(secretHash);
const auto view = gsl::make_span(hash);
const auto key = view.subspan(0, kAesKeyLength);
const auto iv = view.subspan(kAesKeyLength, kAesIvLength);
auto result = AesParams();
base::copy_bytes(result.key, view.subspan(0, kAesKeyLength));
base::copy_bytes(result.iv, view.subspan(kAesKeyLength, kAesIvLength));
return result;
}
base::byte_vector EncryptOrDecrypt(
base::const_byte_span initial,
AesParams &&params,
int encryptOrDecrypt) {
Expects((initial.size() & 0x0F) == 0);
auto aesKey = AES_KEY();
const auto error = (encryptOrDecrypt == AES_ENCRYPT)
? AES_set_encrypt_key(
reinterpret_cast<const uchar*>(params.key.data()),
params.key.size() * CHAR_BIT,
&aesKey)
: AES_set_decrypt_key(
reinterpret_cast<const uchar*>(params.key.data()),
params.key.size() * CHAR_BIT,
&aesKey);
if (error != 0) {
LOG(("App Error: Could not AES_set_encrypt_key, result %1"
).arg(error));
return {};
}
auto result = base::byte_vector(initial.size());
AES_cbc_encrypt(
reinterpret_cast<const uchar*>(initial.data()),
reinterpret_cast<uchar*>(result.data()),
initial.size(),
&aesKey,
reinterpret_cast<uchar*>(params.iv.data()),
encryptOrDecrypt);
return result;
}
base::byte_vector Encrypt(
base::const_byte_span decrypted,
AesParams &&params) {
return EncryptOrDecrypt(decrypted, std::move(params), AES_ENCRYPT);
}
base::byte_vector Decrypt(
base::const_byte_span encrypted,
AesParams &&params) {
return EncryptOrDecrypt(encrypted, std::move(params), AES_DECRYPT);
}
base::byte_vector PasswordHashForSecret(
base::const_byte_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(base::const_byte_span bytes) {
const auto full = std::accumulate(
bytes.begin(),
bytes.end(),
0ULL,
[](uint64 sum, gsl::byte value) { return sum + uchar(value); });
const auto mod = (full % 255ULL);
return (mod == 239);
}
bool CheckSecretBytes(base::const_byte_span secret) {
return CheckBytesMod255(secret);
}
base::byte_vector GenerateSecretBytes() {
auto result = base::byte_vector(kSecretSize);
memset_rand(result.data(), result.size());
const auto full = std::accumulate(
result.begin(),
result.end(),
0ULL,
[](uint64 sum, gsl::byte value) { return sum + uchar(value); });
const auto mod = (full % 255ULL);
const auto add = 255ULL + 239 - mod;
auto first = (static_cast<uchar>(result[0]) + add) % 255ULL;
result[0] = static_cast<gsl::byte>(first);
return result;
}
base::byte_vector DecryptSecretBytes(
base::const_byte_span encryptedSecret,
base::const_byte_span passwordHashForSecret) {
if (encryptedSecret.empty()) {
return {};
} else if (encryptedSecret.size() != kSecretSize) {
LOG(("API Error: Wrong secret size %1"
).arg(encryptedSecret.size()));
return {};
}
auto params = PrepareAesParams(passwordHashForSecret);
auto result = Decrypt(encryptedSecret, std::move(params));
if (!CheckSecretBytes(result)) {
LOG(("API Error: Bad secret bytes."));
return {};
}
return result;
}
base::byte_vector EncryptSecretBytes(
base::const_byte_span secret,
base::const_byte_span passwordHashForSecret) {
Expects(secret.size() == kSecretSize);
Expects(CheckSecretBytes(secret) == true);
auto params = PrepareAesParams(passwordHashForSecret);
return Encrypt(secret, std::move(params));
}
base::byte_vector Concatenate(
base::const_byte_span a,
base::const_byte_span b) {
auto result = base::byte_vector(a.size() + b.size());
base::copy_bytes(result, a);
base::copy_bytes(gsl::make_span(result).subspan(a.size()), b);
return result;
}
EncryptedData EncryptData(
base::const_byte_span dataSecret,
const std::map<QString, QString> &data) {
Expects(dataSecret.size() == kSecretSize);
const auto bytes = SerializeData(data);
constexpr auto kFromPadding = kMinPadding + kAlignTo - 1;
constexpr auto kPaddingDelta = kMaxPadding - kFromPadding;
const auto randomPadding = kFromPadding
+ (rand_value<uint32>() % kPaddingDelta);
const auto padding = randomPadding
- ((bytes.size() + randomPadding) % kAlignTo);
Assert(padding >= kMinPadding && padding <= kMaxPadding);
auto unencrypted = base::byte_vector(padding + bytes.size());
Assert(unencrypted.size() % kAlignTo == 0);
unencrypted[0] = static_cast<gsl::byte>(padding);
memset_rand(unencrypted.data() + 1, padding - 1);
base::copy_bytes(
gsl::make_span(unencrypted).subspan(padding),
bytes);
const auto dataHash = openssl::Sha256(unencrypted);
const auto dataSecretHash = openssl::Sha512(
Concatenate(dataSecret, dataHash));
auto params = PrepareAesParams(dataSecretHash);
return {
{ dataHash.begin(), dataHash.end() },
Encrypt(unencrypted, std::move(params))
};
}
std::map<QString, QString> DecryptData(
base::const_byte_span encrypted,
base::const_byte_span dataHash,
base::const_byte_span dataSecret) {
constexpr auto kDataHashSize = 32;
if (encrypted.empty()) {
return {};
} else if (dataHash.size() != kDataHashSize) {
LOG(("API Error: Bad data hash size %1").arg(dataHash.size()));
return {};
} else if (dataSecret.size() != kSecretSize) {
LOG(("API Error: Bad data secret size %1").arg(dataSecret.size()));
return {};
}
const auto dataSecretHash = openssl::Sha512(
Concatenate(dataSecret, dataHash));
auto params = PrepareAesParams(dataSecretHash);
const auto decrypted = Decrypt(encrypted, std::move(params));
if (base::compare_bytes(openssl::Sha256(decrypted), dataHash) != 0) {
LOG(("API Error: Bad data hash."));
return {};
}
const auto padding = static_cast<uchar>(decrypted[0]);
if (padding < kMinPadding
|| padding > kMaxPadding
|| padding > decrypted.size()) {
LOG(("API Error: Bad padding value %1").arg(padding));
return {};
}
const auto bytes = gsl::make_span(decrypted).subspan(padding);
return DeserializeData(bytes);
}
base::byte_vector PrepareValueHash(
base::const_byte_span dataHash,
base::const_byte_span valueSecret) {
const auto result = openssl::Sha256(Concatenate(dataHash, valueSecret));
return { result.begin(), result.end() };
}
base::byte_vector EncryptValueSecret(
base::const_byte_span valueSecret,
base::const_byte_span secret,
base::const_byte_span valueHash) {
const auto valueSecretHash = openssl::Sha512(
Concatenate(secret, valueHash));
return EncryptSecretBytes(valueSecret, valueSecretHash);
}
base::byte_vector DecryptValueSecret(
base::const_byte_span encrypted,
base::const_byte_span secret,
base::const_byte_span valueHash) {
const auto valueSecretHash = openssl::Sha512(
Concatenate(secret, valueHash));
return DecryptSecretBytes(encrypted, valueSecretHash);
}
} // namespace Passport

View File

@ -0,0 +1,51 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
namespace Passport {
base::byte_vector GenerateSecretBytes();
base::byte_vector EncryptSecretBytes(
base::const_byte_span secret,
base::const_byte_span passwordHashForSecret);
base::byte_vector DecryptSecretBytes(
base::const_byte_span encryptedSecret,
base::const_byte_span passwordHashForSecret);
base::byte_vector PasswordHashForSecret(base::const_byte_span passwordUtf8);
struct EncryptedData {
base::byte_vector hash;
base::byte_vector bytes;
};
EncryptedData EncryptData(
base::const_byte_span dataSecret,
const std::map<QString, QString> &data);
std::map<QString, QString> DecryptData(
base::const_byte_span encrypted,
base::const_byte_span dataHash,
base::const_byte_span dataSecret);
base::byte_vector PrepareValueHash(
base::const_byte_span dataHash,
base::const_byte_span valueSecret);
base::byte_vector EncryptValueSecret(
base::const_byte_span valueSecret,
base::const_byte_span secret,
base::const_byte_span valueHash);
base::byte_vector DecryptValueSecret(
base::const_byte_span encrypted,
base::const_byte_span secret,
base::const_byte_span valueHash);
} // namespace Passport

View File

@ -171,6 +171,9 @@ void FormBox::Inner::refresh() {
bool ready) {
if (_rows.size() <= index) {
_rows.push_back(object_ptr<FormRow>(this, title, description));
_rows[index]->addClickHandler([=] {
_controller->editField(index);
});
}
_rows[index++]->setReady(ready);
});

View File

@ -8,6 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "passport/passport_form_controller.h"
#include "passport/passport_form_box.h"
#include "passport/passport_edit_identity_box.h"
#include "passport/passport_encryption.h"
#include "boxes/confirm_box.h"
#include "lang/lang_keys.h"
#include "base/openssl_help.h"
@ -49,11 +51,15 @@ void FormController::submitPassword(const QString &password) {
if (_passwordCheckRequestId) {
return;
} else if (password.isEmpty()) {
_passwordError.fire(QString());
}
const auto data = _password.salt + password.toUtf8() + _password.salt;
const auto hash = hashSha256(data.constData(), data.size());
const auto passwordBytes = password.toUtf8();
const auto data = _password.salt + passwordBytes + _password.salt;
const auto hash = openssl::Sha256(gsl::as_bytes(gsl::make_span(data)));
_passwordHashForAuth = { hash.begin(), hash.end() };
_passwordCheckRequestId = request(MTPaccount_GetPasswordSettings(
MTP_bytes(gsl::as_bytes(gsl::make_span(hash)))
MTP_bytes(_passwordHashForAuth)
)).handleFloodErrors(
).done([=](const MTPaccount_PasswordSettings &result) {
Expects(result.type() == mtpc_account_passwordSettings);
@ -61,7 +67,14 @@ void FormController::submitPassword(const QString &password) {
_passwordCheckRequestId = 0;
const auto &data = result.c_account_passwordSettings();
_passwordEmail = qs(data.vemail);
_secret = byteVectorFromMTP(data.vsecure_secret);
const auto hash = openssl::Sha512(gsl::as_bytes(gsl::make_span(passwordBytes)));
_passwordHashForSecret = { hash.begin(), hash.end() };
_secret = DecryptSecretBytes(
bytesFromMTP(data.vsecure_secret),
_passwordHashForSecret);
for (auto &field : _form.fields) {
field.data.values = fillData(field.data);
}
_secretReady.fire({});
}).fail([=](const RPCError &error) {
_passwordCheckRequestId = 0;
@ -133,6 +146,142 @@ void FormController::fillRows(
}
}
void FormController::editField(int index) {
Expects(index >= 0 && index < _form.fields.size());
auto box = [&]() -> object_ptr<BoxContent> {
const auto &field = _form.fields[index];
switch (field.type) {
case Field::Type::Identity:
return Box<IdentityBox>(this, index, fieldDataIdentity(field));
}
return { nullptr };
}();
if (box) {
_editBox = Ui::show(std::move(box), LayerOption::KeepOther);
}
}
IdentityData FormController::fieldDataIdentity(const Field &field) const {
const auto &map = field.data.values;
auto result = IdentityData();
if (const auto i = map.find(qsl("first_name")); i != map.cend()) {
result.name = i->second;
}
if (const auto i = map.find(qsl("last_name")); i != map.cend()) {
result.surname = i->second;
}
return result;
}
void FormController::saveFieldIdentity(
int index,
const IdentityData &data) {
Expects(_editBox != nullptr);
Expects(index >= 0 && index < _form.fields.size());
Expects(_form.fields[index].type == Field::Type::Identity);
_form.fields[index].data.values[qsl("first_name")] = data.name;
_form.fields[index].data.values[qsl("last_name")] = data.surname;
saveData(index);
_editBox->closeBox();
}
std::map<QString, QString> FormController::fillData(
const Value &from) const {
if (from.data.isEmpty()) {
return {};
}
const auto valueHash = gsl::as_bytes(gsl::make_span(from.dataHash));
const auto valueSecret = DecryptValueSecret(
gsl::as_bytes(gsl::make_span(from.dataSecret)),
_secret,
valueHash);
return DecryptData(
gsl::as_bytes(gsl::make_span(from.data)),
valueHash,
valueSecret);
}
void FormController::saveData(int index) {
Expects(index >= 0 && index < _form.fields.size());
if (_secret.empty()) {
generateSecret([=] {
saveData(index);
});
return;
}
const auto &data = _form.fields[index].data;
const auto valueSecret = GenerateSecretBytes();
const auto encrypted = EncryptData(valueSecret, data.values);
// #TODO file_hash + file_hash + ...
// PrepareValueHash(encrypted.hash, valueSecret);
const auto valueHash = encrypted.hash;
auto valueHashString = QString();
valueHashString.reserve(valueHash.size() * 2);
const auto hex = [](uchar value) -> QChar {
return (value >= 10) ? ('a' + (value - 10)) : ('0' + value);
};
for (const auto byte : valueHash) {
const auto value = uchar(byte);
const auto high = uchar(value / 16);
const auto low = uchar(value % 16);
valueHashString.append(hex(high)).append(hex(low));
}
const auto encryptedValueSecret = EncryptValueSecret(
valueSecret,
_secret,
valueHash);
request(MTPaccount_SaveSecureValue(MTP_inputSecureValueData(
MTP_string(data.name),
MTP_bytes(encrypted.bytes),
MTP_string(valueHashString),
MTP_bytes(encryptedValueSecret)
))).done([=](const MTPaccount_SecureValueResult &result) {
if (result.type() == mtpc_account_secureValueResultSaved) {
Ui::show(Box<InformBox>("Saved"), LayerOption::KeepOther);
} else if (result.type() == mtpc_account_secureValueVerificationNeeded) {
Ui::show(Box<InformBox>("Verification needed :("), LayerOption::KeepOther);
}
}).fail([=](const RPCError &error) {
// #TODO
}).send();
}
void FormController::generateSecret(base::lambda<void()> callback) {
if (_saveSecretRequestId) {
return;
}
auto secret = GenerateSecretBytes();
auto encryptedSecret = EncryptSecretBytes(
secret,
_passwordHashForSecret);
using Flag = MTPDaccount_passwordInputSettings::Flag;
_saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings(
MTP_bytes(_passwordHashForAuth),
MTP_account_passwordInputSettings(
MTP_flags(Flag::f_new_secure_secret),
MTPbytes(), // new_salt
MTPbytes(), // new_password_hash
MTPstring(), // hint
MTPstring(), // email
MTP_bytes(encryptedSecret))
)).done([=](const MTPBool &result) {
_saveSecretRequestId = 0;
_secret = secret;
callback();
}).fail([=](const RPCError &error) {
// #TODO wrong password hash error?
Ui::show(Box<InformBox>("Saving encrypted value failed."));
_saveSecretRequestId = 0;
}).send();
}
void FormController::requestForm() {
auto scope = QVector<MTPstring>();
scope.reserve(_request.scope.size());
@ -171,7 +320,30 @@ auto FormController::convertValue(
const auto &data = value.c_secureValueData();
result.name = qs(data.vname);
result.data = data.vdata.v;
result.dataHash = data.vhash.v;
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.dataHash.push_back(byte);
}
}
if (result.dataHash.size() != 32) {
result.dataHash.clear();
}
// result.dataHash = data.vhash.v;
result.dataSecret = data.vsecret.v;
} break;
case mtpc_secureValueText: {

View File

@ -9,6 +9,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/sender.h"
class BoxContent;
namespace Window {
class Controller;
} // namespace Window
@ -29,13 +31,10 @@ struct FormRequest {
};
struct IdentityData;
class FormController : private MTP::Sender {
public:
struct PasswordCheckResult {
QByteArray secret;
};
FormController(
not_null<Window::Controller*> controller,
const FormRequest &request);
@ -56,6 +55,9 @@ public:
QString title,
QString description,
bool ready)> callback);
void editField(int index);
void saveFieldIdentity(int index, const IdentityData &data);
private:
struct File {
@ -71,6 +73,7 @@ private:
QByteArray data;
QByteArray dataHash;
QByteArray dataSecret;
std::map<QString, QString> values;
QString text;
QByteArray textHash;
@ -118,6 +121,12 @@ private:
void parsePassword(const MTPDaccount_noPassword &settings);
void parsePassword(const MTPDaccount_password &settings);
IdentityData fieldDataIdentity(const Field &field) const;
std::map<QString, QString> fillData(const Value &from) const;
void saveData(int index);
void generateSecret(base::lambda<void()> callback);
not_null<Window::Controller*> _controller;
FormRequest _request;
UserData *_bot = nullptr;
@ -130,11 +139,16 @@ private:
PasswordSettings _password;
Form _form;
base::byte_vector _passwordHashForSecret;
base::byte_vector _passwordHashForAuth;
base::byte_vector _secret;
mtpRequestId _saveSecretRequestId = 0;
QString _passwordEmail;
rpl::event_stream<> _secretReady;
rpl::event_stream<QString> _passwordError;
QPointer<BoxContent> _editBox;
};
} // namespace Passport

View File

@ -454,6 +454,10 @@
<(src_loc)/mtproto/type_utils.h
<(src_loc)/overview/overview_layout.cpp
<(src_loc)/overview/overview_layout.h
<(src_loc)/passport/passport_edit_identity_box.cpp
<(src_loc)/passport/passport_edit_identity_box.h
<(src_loc)/passport/passport_encryption.cpp
<(src_loc)/passport/passport_encryption.h
<(src_loc)/passport/passport_form_box.cpp
<(src_loc)/passport/passport_form_box.h
<(src_loc)/passport/passport_form_controller.cpp