ZaStoGram_desktop/Telegram/SourceFiles/wallet/wallet_phrase_shares.cpp

147 lines
4.2 KiB
C++

/*
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 "wallet/wallet_phrase_shares.h"
#include "base/random.h"
#include "mtproto/core_types.h"
#include "tde2e/tde2e_api.h"
namespace Wallet::PhraseShares {
namespace {
[[nodiscard]] std::optional<QByteArray> ParseDecryptedKeyPart(
const QByteArray &plain) {
if (plain.isEmpty() || (plain.size() % sizeof(mtpPrime))) {
LOG(("Wallet Error: decrypted share invalid TL size=%1.")
.arg(plain.size()));
return std::nullopt;
}
auto buffer = mtpBuffer();
buffer.resize(plain.size() / sizeof(mtpPrime));
memcpy(buffer.data(), plain.constData(), plain.size());
const mtpPrime *from = buffer.constData();
const auto end = from + buffer.size();
if (uint32(*from++) != kDecryptedKeyPartId) {
LOG(("Wallet Error: decrypted share unexpected TL constructor."));
return std::nullopt;
}
auto share = MTPstring();
if (!share.read(from, end) || from != end) {
LOG(("Wallet Error: decrypted share invalid TL string or trailing "
"data; plain_bytes=%1.").arg(plain.size()));
return std::nullopt;
}
return share.v;
}
[[nodiscard]] QByteArray SerializeDecryptedKeyPart(const QByteArray &share) {
auto buffer = mtpBuffer();
buffer.push_back(mtpPrime(kDecryptedKeyPartId));
MTP_bytes(share).write(buffer);
return QByteArray(
reinterpret_cast<const char*>(buffer.constData()),
buffer.size() * sizeof(mtpPrime));
}
} // namespace
std::optional<QByteArray> DecryptShare(
const TdE2E::TemporaryKeyPair &keys,
const QByteArray &data) {
if (data.size() <= kPublicKeySize) {
LOG(("Wallet Error: encrypted share too short bytes=%1 "
"public_key_bytes=%2.").arg(data.size()).arg(kPublicKeySize));
return std::nullopt;
}
const auto plain = keys.decryptForOne(
data.left(kPublicKeySize),
data.mid(kPublicKeySize));
if (!plain) {
LOG(("Wallet Error: share decryption failed encrypted_bytes=%1.")
.arg(data.size()));
return std::nullopt;
}
return ParseDecryptedKeyPart(*plain);
}
std::optional<QByteArray> CombineShares(
const std::vector<QByteArray> &shares) {
if (shares.empty() || shares.front().isEmpty()) {
LOG(("Wallet Error: share combine empty input count=%1 "
"first_bytes=%2."
).arg(shares.size()
).arg(shares.empty() ? 0 : shares.front().size()));
return std::nullopt;
}
const auto length = shares.front().size();
auto result = QByteArray(length, '\0');
const auto to = result.data();
for (const auto &share : shares) {
if (share.size() != length) {
LOG(("Wallet Error: share combine length mismatch "
"expected_bytes=%1 actual_bytes=%2 count=%3."
).arg(length).arg(share.size()).arg(shares.size()));
return std::nullopt;
}
const auto from = share.constData();
for (auto i = 0; i != length; ++i) {
to[i] ^= from[i];
}
}
return result;
}
QByteArray SeedFromWords(const std::vector<QString> &words) {
const auto joined = QStringList(words.begin(), words.end())
.join(QChar(' '))
.toUtf8();
if (joined.size() > kSeedLength) {
return QByteArray();
}
return joined.leftJustified(kSeedLength, ' ');
}
std::vector<QByteArray> SplitSeed(const QByteArray &seed, int count) {
Expects(count >= 2 && !seed.isEmpty());
const auto length = seed.size();
auto result = std::vector<QByteArray>(count, QByteArray(length, '\0'));
auto last = seed;
const auto to = last.data();
for (auto i = 0; i != count - 1; ++i) {
auto &share = result[i];
base::RandomFill(bytes::make_detached_span(share));
const auto from = share.constData();
for (auto j = 0; j != length; ++j) {
to[j] ^= from[j];
}
}
result.back() = std::move(last);
return result;
}
std::optional<QByteArray> EncryptShare(
const QByteArray &holderPublicKey,
const QByteArray &share) {
if (holderPublicKey.size() != kPublicKeySize) {
return std::nullopt;
}
const auto pair = TdE2E::TemporaryKeyPair::Generate();
if (!pair) {
return std::nullopt;
}
const auto cipher = pair->encryptForOne(
holderPublicKey,
SerializeDecryptedKeyPart(share));
if (!cipher) {
return std::nullopt;
}
return pair->publicKey() + *cipher;
}
} // namespace Wallet::PhraseShares