mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Implement new client hello generation.
This commit is contained in:
parent
c890281258
commit
60fe961c21
2 changed files with 248 additions and 105 deletions
|
@ -111,6 +111,7 @@ tlsBlockDomain = TlsBlock;
|
||||||
tlsBlockGrease seed:int = TlsBlock;
|
tlsBlockGrease seed:int = TlsBlock;
|
||||||
tlsBlockPublicKey = TlsBlock;
|
tlsBlockPublicKey = TlsBlock;
|
||||||
tlsBlockScope entries:Vector<TlsBlock> = TlsBlock;
|
tlsBlockScope entries:Vector<TlsBlock> = TlsBlock;
|
||||||
|
tlsBlockPermutation entries:Vector<Vector<TlsBlock>> = TlsBlock;
|
||||||
|
|
||||||
---functions---
|
---functions---
|
||||||
|
|
||||||
|
|
|
@ -35,14 +35,22 @@ using BigNum = openssl::BigNum;
|
||||||
using BigNumContext = openssl::Context;
|
using BigNumContext = openssl::Context;
|
||||||
|
|
||||||
[[nodiscard]] MTPTlsClientHello PrepareClientHelloRules() {
|
[[nodiscard]] MTPTlsClientHello PrepareClientHelloRules() {
|
||||||
auto stack = std::vector<QVector<MTPTlsBlock>>();
|
using Scope = QVector<MTPTlsBlock>;
|
||||||
|
using Permutation = std::vector<Scope>;
|
||||||
|
using StackElement = std::variant<Scope, Permutation>;
|
||||||
|
auto stack = std::vector<StackElement>();
|
||||||
const auto pushToBack = [&](MTPTlsBlock &&block) {
|
const auto pushToBack = [&](MTPTlsBlock &&block) {
|
||||||
Expects(!stack.empty());
|
Expects(!stack.empty());
|
||||||
|
|
||||||
stack.back().push_back(std::move(block));
|
if (const auto scope = std::get_if<Scope>(&stack.back())) {
|
||||||
|
scope->push_back(std::move(block));
|
||||||
|
} else {
|
||||||
|
auto &permutation = v::get<Permutation>(stack.back());
|
||||||
|
Assert(!permutation.empty());
|
||||||
|
permutation.back().push_back(std::move(block));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const auto S = [&](QLatin1String s) {
|
const auto S = [&](QByteArray data) {
|
||||||
const auto data = QByteArray(s.data(), s.size());
|
|
||||||
pushToBack(MTP_tlsBlockString(MTP_bytes(data)));
|
pushToBack(MTP_tlsBlockString(MTP_bytes(data)));
|
||||||
};
|
};
|
||||||
const auto Z = [&](int length) {
|
const auto Z = [&](int length) {
|
||||||
|
@ -60,60 +68,130 @@ using BigNumContext = openssl::Context;
|
||||||
const auto K = [&] {
|
const auto K = [&] {
|
||||||
pushToBack(MTP_tlsBlockPublicKey());
|
pushToBack(MTP_tlsBlockPublicKey());
|
||||||
};
|
};
|
||||||
const auto Open = [&] {
|
const auto OpenScope = [&] {
|
||||||
stack.emplace_back();
|
stack.emplace_back(Scope());
|
||||||
};
|
};
|
||||||
const auto Close = [&] {
|
const auto CloseScope = [&] {
|
||||||
Expects(stack.size() > 1);
|
Expects(stack.size() > 1);
|
||||||
|
Expects(v::is<Scope>(stack.back()));
|
||||||
|
|
||||||
const auto blocks = std::move(stack.back());
|
const auto blocks = std::move(v::get<Scope>(stack.back()));
|
||||||
stack.pop_back();
|
stack.pop_back();
|
||||||
pushToBack(MTP_tlsBlockScope(MTP_vector<MTPTlsBlock>(blocks)));
|
pushToBack(MTP_tlsBlockScope(MTP_vector<MTPTlsBlock>(blocks)));
|
||||||
};
|
};
|
||||||
|
const auto OpenPermutation = [&] {
|
||||||
|
stack.emplace_back(Permutation());
|
||||||
|
};
|
||||||
|
const auto ClosePermutation = [&] {
|
||||||
|
Expects(stack.size() > 1);
|
||||||
|
Expects(v::is<Permutation>(stack.back()));
|
||||||
|
|
||||||
|
const auto list = std::move(v::get<Permutation>(stack.back()));
|
||||||
|
stack.pop_back();
|
||||||
|
|
||||||
|
const auto wrapped = list | ranges::views::transform([](
|
||||||
|
const QVector<MTPTlsBlock> &elements) {
|
||||||
|
return MTP_vector<MTPTlsBlock>(elements);
|
||||||
|
}) | ranges::to<QVector<MTPVector<MTPTlsBlock>>>();
|
||||||
|
|
||||||
|
pushToBack(MTP_tlsBlockPermutation(
|
||||||
|
MTP_vector<MTPVector<MTPTlsBlock>>(wrapped)));
|
||||||
|
};
|
||||||
|
const auto StartPermutationElement = [&] {
|
||||||
|
Expects(stack.size() > 1);
|
||||||
|
Expects(v::is<Permutation>(stack.back()));
|
||||||
|
|
||||||
|
v::get<Permutation>(stack.back()).emplace_back();
|
||||||
|
};
|
||||||
const auto Finish = [&] {
|
const auto Finish = [&] {
|
||||||
Expects(stack.size() == 1);
|
Expects(stack.size() == 1);
|
||||||
|
Expects(v::is<Scope>(stack.back()));
|
||||||
|
|
||||||
return stack.back();
|
return v::get<Scope>(stack.back());
|
||||||
};
|
};
|
||||||
|
|
||||||
stack.emplace_back();
|
stack.emplace_back(Scope());
|
||||||
|
|
||||||
S(qstr("\x16\x03\x01\x02\x00\x01\x00\x01\xfc\x03\x03"));
|
S("\x16\x03\x01\x02\x00\x01\x00\x01\xfc\x03\x03"_q);
|
||||||
Z(32);
|
Z(32);
|
||||||
S(qstr("\x20"));
|
S("\x20"_q);
|
||||||
R(32);
|
R(32);
|
||||||
S(qstr("\x00\x20"));
|
S("\x00\x20"_q);
|
||||||
G(0);
|
G(0);
|
||||||
S(qstr(""
|
S(""
|
||||||
"\x13\x01\x13\x02\x13\x03\xc0\x2b\xc0\x2f\xc0\x2c\xc0\x30\xcc\xa9"
|
"\x13\x01\x13\x02\x13\x03\xc0\x2b\xc0\x2f\xc0\x2c\xc0\x30\xcc\xa9"
|
||||||
"\xcc\xa8\xc0\x13\xc0\x14\x00\x9c\x00\x9d\x00\x2f\x00\x35\x01\x00"
|
"\xcc\xa8\xc0\x13\xc0\x14\x00\x9c\x00\x9d\x00\x2f\x00\x35\x01\x00"
|
||||||
"\x01\x93"));
|
"\x01\x93"_q);
|
||||||
G(2);
|
G(2);
|
||||||
S(qstr("\x00\x00\x00\x00"));
|
S("\x00\x00"_q);
|
||||||
Open();
|
OpenPermutation(); {
|
||||||
Open();
|
StartPermutationElement(); {
|
||||||
S(qstr("\x00"));
|
S("\x00\x00"_q);
|
||||||
Open();
|
OpenScope();
|
||||||
D();
|
OpenScope();
|
||||||
Close();
|
S("\x00"_q);
|
||||||
Close();
|
OpenScope();
|
||||||
Close();
|
D();
|
||||||
S(qstr("\x00\x17\x00\x00\xff\x01\x00\x01\x00\x00\x0a\x00\x0a\x00\x08"));
|
CloseScope();
|
||||||
G(4);
|
CloseScope();
|
||||||
S(qstr(""
|
CloseScope();
|
||||||
"\x00\x1d\x00\x17\x00\x18\x00\x0b\x00\x02\x01\x00\x00\x23\x00\x00"
|
}
|
||||||
"\x00\x10\x00\x0e\x00\x0c\x02\x68\x32\x08\x68\x74\x74\x70\x2f\x31"
|
StartPermutationElement(); {
|
||||||
"\x2e\x31\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\x0d\x00\x12\x00"
|
S("\x00\x05\x00\x05\x01\x00\x00\x00\x00"_q);
|
||||||
"\x10\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06"
|
}
|
||||||
"\x01\x00\x12\x00\x00\x00\x33\x00\x2b\x00\x29"));
|
StartPermutationElement(); {
|
||||||
G(4);
|
S("\x00\x0a\x00\x0a\x00\x08"_q);
|
||||||
S(qstr("\x00\x01\x00\x00\x1d\x00\x20"));
|
G(4);
|
||||||
K();
|
S("\x00\x1d\x00\x17\x00\x18"_q);
|
||||||
S(qstr("\x00\x2d\x00\x02\x01\x01\x00\x2b\x00\x0b\x0a"));
|
}
|
||||||
G(6);
|
StartPermutationElement(); {
|
||||||
S(qstr("\x03\x04\x03\x03\x03\x02\x03\x01\x00\x1b\x00\x03\x02\x00\x02"));
|
S("\x00\x0b\x00\x02\x01\x00"_q);
|
||||||
|
}
|
||||||
|
StartPermutationElement(); {
|
||||||
|
S(""
|
||||||
|
"\x00\x0d\x00\x12\x00\x10\x04\x03\x08\x04\x04\x01\x05\x03"
|
||||||
|
"\x08\x05\x05\x01\x08\x06\x06\x01"_q);
|
||||||
|
}
|
||||||
|
StartPermutationElement(); {
|
||||||
|
S(""
|
||||||
|
"\x00\x10\x00\x0e\x00\x0c\x02\x68\x32\x08\x68\x74\x74\x70"
|
||||||
|
"\x2f\x31\x2e\x31"_q);
|
||||||
|
}
|
||||||
|
StartPermutationElement(); {
|
||||||
|
S("\x00\x12\x00\x00"_q);
|
||||||
|
}
|
||||||
|
StartPermutationElement(); {
|
||||||
|
S("\x00\x17\x00\x00"_q);
|
||||||
|
}
|
||||||
|
StartPermutationElement(); {
|
||||||
|
S("\x00\x1b\x00\x03\x02\x00\x02"_q);
|
||||||
|
}
|
||||||
|
StartPermutationElement(); {
|
||||||
|
S("\x00\x23\x00\x00"_q);
|
||||||
|
}
|
||||||
|
StartPermutationElement(); {
|
||||||
|
S("\x00\x2b\x00\x07\x06"_q);
|
||||||
|
G(6);
|
||||||
|
S("\x03\x04\x03\x03"_q);
|
||||||
|
}
|
||||||
|
StartPermutationElement(); {
|
||||||
|
S("\x00\x2d\x00\x02\x01\x01"_q);
|
||||||
|
}
|
||||||
|
StartPermutationElement(); {
|
||||||
|
S("\x00\x33\x00\x2b\x00\x29"_q);
|
||||||
|
G(4);
|
||||||
|
S("\x00\x01\x00\x00\x1d\x00\x20"_q);
|
||||||
|
K();
|
||||||
|
}
|
||||||
|
StartPermutationElement(); {
|
||||||
|
S("\x44\x69\x00\x05\x00\x03\x02\x68\x32"_q);
|
||||||
|
}
|
||||||
|
StartPermutationElement(); {
|
||||||
|
S("\xff\x01\x00\x01\x00"_q);
|
||||||
|
}
|
||||||
|
} ClosePermutation();
|
||||||
G(3);
|
G(3);
|
||||||
S(qstr("\x00\x01\x00\x00\x15"));
|
S("\x00\x01\x00\x00\x15"_q);
|
||||||
|
|
||||||
return MTP_tlsClientHello(MTP_vector<MTPTlsBlock>(Finish()));
|
return MTP_tlsClientHello(MTP_vector<MTPTlsBlock>(Finish()));
|
||||||
}
|
}
|
||||||
|
@ -228,63 +306,78 @@ struct ClientHello {
|
||||||
QByteArray digest;
|
QByteArray digest;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ClientHelloGenerator {
|
class Generator {
|
||||||
public:
|
public:
|
||||||
ClientHelloGenerator(
|
Generator(
|
||||||
const MTPTlsClientHello &rules,
|
const MTPTlsClientHello &rules,
|
||||||
bytes::const_span domain,
|
bytes::const_span domain,
|
||||||
bytes::const_span key);
|
bytes::const_span key);
|
||||||
[[nodiscard]] ClientHello result();
|
[[nodiscard]] ClientHello take();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
[[nodiscard]] bytes::span grow(int size);
|
class Part final {
|
||||||
void writeBlocks(const QVector<MTPTlsBlock> &blocks);
|
public:
|
||||||
void writeBlock(const MTPTlsBlock &data);
|
explicit Part(
|
||||||
void writeBlock(const MTPDtlsBlockString &data);
|
bytes::const_span domain,
|
||||||
void writeBlock(const MTPDtlsBlockZero &data);
|
const bytes::vector &greases);
|
||||||
void writeBlock(const MTPDtlsBlockGrease &data);
|
|
||||||
void writeBlock(const MTPDtlsBlockRandom &data);
|
[[nodiscard]] bytes::span grow(int size);
|
||||||
void writeBlock(const MTPDtlsBlockDomain &data);
|
void writeBlocks(const QVector<MTPTlsBlock> &blocks);
|
||||||
void writeBlock(const MTPDtlsBlockPublicKey &data);
|
void writeBlock(const MTPTlsBlock &data);
|
||||||
void writeBlock(const MTPDtlsBlockScope &data);
|
void writeBlock(const MTPDtlsBlockString &data);
|
||||||
void writePadding();
|
void writeBlock(const MTPDtlsBlockZero &data);
|
||||||
void writeDigest();
|
void writeBlock(const MTPDtlsBlockGrease &data);
|
||||||
void writeTimestamp();
|
void writeBlock(const MTPDtlsBlockRandom &data);
|
||||||
|
void writeBlock(const MTPDtlsBlockDomain &data);
|
||||||
|
void writeBlock(const MTPDtlsBlockPublicKey &data);
|
||||||
|
void writeBlock(const MTPDtlsBlockScope &data);
|
||||||
|
void writeBlock(const MTPDtlsBlockPermutation &data);
|
||||||
|
void finalize(bytes::const_span key);
|
||||||
|
[[nodiscard]] QByteArray extractDigest() const;
|
||||||
|
|
||||||
|
[[nodiscard]] bool error() const;
|
||||||
|
[[nodiscard]] QByteArray take();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void writePadding();
|
||||||
|
void writeDigest(bytes::const_span key);
|
||||||
|
void injectTimestamp();
|
||||||
|
|
||||||
|
bytes::const_span _domain;
|
||||||
|
const bytes::vector &_greases;
|
||||||
|
QByteArray _result;
|
||||||
|
const char *_data = nullptr;
|
||||||
|
int _digestPosition = -1;
|
||||||
|
bool _error = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
bytes::const_span _domain;
|
|
||||||
bytes::const_span _key;
|
|
||||||
bytes::vector _greases;
|
bytes::vector _greases;
|
||||||
std::vector<int> _scopeStack;
|
Part _result;
|
||||||
QByteArray _result;
|
|
||||||
QByteArray _digest;
|
QByteArray _digest;
|
||||||
int _digestPosition = -1;
|
|
||||||
bool _error = false;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ClientHelloGenerator::ClientHelloGenerator(
|
Generator::Part::Part(
|
||||||
const MTPTlsClientHello &rules,
|
|
||||||
bytes::const_span domain,
|
bytes::const_span domain,
|
||||||
bytes::const_span key)
|
const bytes::vector &greases)
|
||||||
: _domain(domain)
|
: _domain(domain)
|
||||||
, _key(key)
|
, _greases(greases) {
|
||||||
, _greases(PrepareGreases()) {
|
|
||||||
_result.reserve(kClientHelloLength);
|
_result.reserve(kClientHelloLength);
|
||||||
writeBlocks(rules.match([&](const MTPDtlsClientHello &data) {
|
_data = _result.constData();
|
||||||
return data.vblocks().v;
|
|
||||||
}));
|
|
||||||
writePadding();
|
|
||||||
writeDigest();
|
|
||||||
writeTimestamp();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ClientHello ClientHelloGenerator::result() {
|
bool Generator::Part::error() const {
|
||||||
return {
|
return _error;
|
||||||
_error ? QByteArray() : std::move(_result),
|
|
||||||
_error ? QByteArray() : std::move(_digest) };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes::span ClientHelloGenerator::grow(int size) {
|
QByteArray Generator::Part::take() {
|
||||||
|
Expects(_error || _result.constData() == _data);
|
||||||
|
|
||||||
|
return _error ? QByteArray() : std::move(_result);
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes::span Generator::Part::grow(int size) {
|
||||||
if (_error
|
if (_error
|
||||||
|| size <= 0
|
|| size <= 0
|
||||||
|| _result.size() + size > kClientHelloLength) {
|
|| _result.size() + size > kClientHelloLength) {
|
||||||
|
@ -297,19 +390,19 @@ bytes::span ClientHelloGenerator::grow(int size) {
|
||||||
return bytes::make_detached_span(_result).subspan(offset);
|
return bytes::make_detached_span(_result).subspan(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientHelloGenerator::writeBlocks(const QVector<MTPTlsBlock> &blocks) {
|
void Generator::Part::writeBlocks(const QVector<MTPTlsBlock> &blocks) {
|
||||||
for (const auto &block : blocks) {
|
for (const auto &block : blocks) {
|
||||||
writeBlock(block);
|
writeBlock(block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientHelloGenerator::writeBlock(const MTPTlsBlock &data) {
|
void Generator::Part::writeBlock(const MTPTlsBlock &data) {
|
||||||
data.match([&](const auto &data) {
|
data.match([&](const auto &data) {
|
||||||
writeBlock(data);
|
writeBlock(data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientHelloGenerator::writeBlock(const MTPDtlsBlockString &data) {
|
void Generator::Part::writeBlock(const MTPDtlsBlockString &data) {
|
||||||
const auto &bytes = data.vdata().v;
|
const auto &bytes = data.vdata().v;
|
||||||
const auto storage = grow(bytes.size());
|
const auto storage = grow(bytes.size());
|
||||||
if (storage.empty()) {
|
if (storage.empty()) {
|
||||||
|
@ -318,7 +411,7 @@ void ClientHelloGenerator::writeBlock(const MTPDtlsBlockString &data) {
|
||||||
bytes::copy(storage, bytes::make_span(bytes));
|
bytes::copy(storage, bytes::make_span(bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientHelloGenerator::writeBlock(const MTPDtlsBlockZero &data) {
|
void Generator::Part::writeBlock(const MTPDtlsBlockZero &data) {
|
||||||
const auto length = data.vlength().v;
|
const auto length = data.vlength().v;
|
||||||
const auto already = _result.size();
|
const auto already = _result.size();
|
||||||
const auto storage = grow(length);
|
const auto storage = grow(length);
|
||||||
|
@ -331,7 +424,7 @@ void ClientHelloGenerator::writeBlock(const MTPDtlsBlockZero &data) {
|
||||||
bytes::set_with_const(storage, bytes::type(0));
|
bytes::set_with_const(storage, bytes::type(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientHelloGenerator::writeBlock(const MTPDtlsBlockGrease &data) {
|
void Generator::Part::writeBlock(const MTPDtlsBlockGrease &data) {
|
||||||
const auto seed = data.vseed().v;
|
const auto seed = data.vseed().v;
|
||||||
if (seed < 0 || seed >= _greases.size()) {
|
if (seed < 0 || seed >= _greases.size()) {
|
||||||
_error = true;
|
_error = true;
|
||||||
|
@ -344,7 +437,7 @@ void ClientHelloGenerator::writeBlock(const MTPDtlsBlockGrease &data) {
|
||||||
bytes::set_with_const(storage, _greases[seed]);
|
bytes::set_with_const(storage, _greases[seed]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientHelloGenerator::writeBlock(const MTPDtlsBlockRandom &data) {
|
void Generator::Part::writeBlock(const MTPDtlsBlockRandom &data) {
|
||||||
const auto length = data.vlength().v;
|
const auto length = data.vlength().v;
|
||||||
const auto storage = grow(length);
|
const auto storage = grow(length);
|
||||||
if (storage.empty()) {
|
if (storage.empty()) {
|
||||||
|
@ -353,7 +446,7 @@ void ClientHelloGenerator::writeBlock(const MTPDtlsBlockRandom &data) {
|
||||||
bytes::set_random(storage);
|
bytes::set_random(storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientHelloGenerator::writeBlock(const MTPDtlsBlockDomain &data) {
|
void Generator::Part::writeBlock(const MTPDtlsBlockDomain &data) {
|
||||||
const auto storage = grow(_domain.size());
|
const auto storage = grow(_domain.size());
|
||||||
if (storage.empty()) {
|
if (storage.empty()) {
|
||||||
return;
|
return;
|
||||||
|
@ -361,7 +454,7 @@ void ClientHelloGenerator::writeBlock(const MTPDtlsBlockDomain &data) {
|
||||||
bytes::copy(storage, _domain);
|
bytes::copy(storage, _domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientHelloGenerator::writeBlock(const MTPDtlsBlockPublicKey &data) {
|
void Generator::Part::writeBlock(const MTPDtlsBlockPublicKey &data) {
|
||||||
const auto key = GeneratePublicKey();
|
const auto key = GeneratePublicKey();
|
||||||
const auto storage = grow(key.size());
|
const auto storage = grow(key.size());
|
||||||
if (storage.empty()) {
|
if (storage.empty()) {
|
||||||
|
@ -370,7 +463,7 @@ void ClientHelloGenerator::writeBlock(const MTPDtlsBlockPublicKey &data) {
|
||||||
bytes::copy(storage, key);
|
bytes::copy(storage, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientHelloGenerator::writeBlock(const MTPDtlsBlockScope &data) {
|
void Generator::Part::writeBlock(const MTPDtlsBlockScope &data) {
|
||||||
const auto storage = grow(kLengthSize);
|
const auto storage = grow(kLengthSize);
|
||||||
if (storage.empty()) {
|
if (storage.empty()) {
|
||||||
return;
|
return;
|
||||||
|
@ -381,27 +474,66 @@ void ClientHelloGenerator::writeBlock(const MTPDtlsBlockScope &data) {
|
||||||
bytes::copy(storage, bytes::object_as_span(&length));
|
bytes::copy(storage, bytes::object_as_span(&length));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientHelloGenerator::writePadding() {
|
void Generator::Part::writeBlock(const MTPDtlsBlockPermutation &data) {
|
||||||
|
auto list = std::vector<QByteArray>();
|
||||||
|
list.reserve(data.ventries().v.size());
|
||||||
|
for (const auto &inner : data.ventries().v) {
|
||||||
|
auto part = Part(_domain, _greases);
|
||||||
|
part.writeBlocks(inner.v);
|
||||||
|
if (part.error()) {
|
||||||
|
_error = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
list.push_back(part.take());
|
||||||
|
}
|
||||||
|
ranges::shuffle(list);
|
||||||
|
for (const auto &element : list) {
|
||||||
|
const auto storage = grow(element.size());
|
||||||
|
if (storage.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bytes::copy(storage, bytes::make_span(element));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Generator::Part::finalize(bytes::const_span key) {
|
||||||
|
if (_error) {
|
||||||
|
return;
|
||||||
|
} else if (_digestPosition < 0) {
|
||||||
|
_error = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
writePadding();
|
||||||
|
writeDigest(key);
|
||||||
|
injectTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray Generator::Part::extractDigest() const {
|
||||||
|
if (_digestPosition < 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return _result.mid(_digestPosition, kHelloDigestLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Generator::Part::writePadding() {
|
||||||
|
Expects(_result.size() <= kClientHelloLength - kLengthSize);
|
||||||
|
|
||||||
const auto padding = kClientHelloLength - kLengthSize - _result.size();
|
const auto padding = kClientHelloLength - kLengthSize - _result.size();
|
||||||
writeBlock(MTP_tlsBlockScope(
|
writeBlock(MTP_tlsBlockScope(
|
||||||
MTP_vector<MTPTlsBlock>(1, MTP_tlsBlockZero(MTP_int(padding)))));
|
MTP_vector<MTPTlsBlock>(1, MTP_tlsBlockZero(MTP_int(padding)))));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientHelloGenerator::writeDigest() {
|
void Generator::Part::writeDigest(bytes::const_span key) {
|
||||||
if (_digestPosition < 0) {
|
Expects(_digestPosition >= 0);
|
||||||
_error = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bytes::copy(
|
bytes::copy(
|
||||||
bytes::make_detached_span(_result).subspan(_digestPosition),
|
bytes::make_detached_span(_result).subspan(_digestPosition),
|
||||||
openssl::HmacSha256(_key, bytes::make_span(_result)));
|
openssl::HmacSha256(key, bytes::make_span(_result)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClientHelloGenerator::writeTimestamp() {
|
void Generator::Part::injectTimestamp() {
|
||||||
if (_digestPosition < 0) {
|
Expects(_digestPosition >= 0);
|
||||||
_error = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto storage = bytes::make_detached_span(_result).subspan(
|
const auto storage = bytes::make_detached_span(_result).subspan(
|
||||||
_digestPosition + kHelloDigestLength - sizeof(int32),
|
_digestPosition + kHelloDigestLength - sizeof(int32),
|
||||||
sizeof(int32));
|
sizeof(int32));
|
||||||
|
@ -409,20 +541,30 @@ void ClientHelloGenerator::writeTimestamp() {
|
||||||
bytes::copy(bytes::object_as_span(&already), storage);
|
bytes::copy(bytes::object_as_span(&already), storage);
|
||||||
already ^= qToLittleEndian(int32(base::unixtime::http_now()));
|
already ^= qToLittleEndian(int32(base::unixtime::http_now()));
|
||||||
bytes::copy(storage, bytes::object_as_span(&already));
|
bytes::copy(storage, bytes::object_as_span(&already));
|
||||||
|
}
|
||||||
|
|
||||||
_digest = QByteArray(kHelloDigestLength, Qt::Uninitialized);
|
Generator::Generator(
|
||||||
bytes::copy(
|
const MTPTlsClientHello &rules,
|
||||||
bytes::make_detached_span(_digest),
|
bytes::const_span domain,
|
||||||
bytes::make_detached_span(_result).subspan(
|
bytes::const_span key)
|
||||||
_digestPosition,
|
: _greases(PrepareGreases())
|
||||||
kHelloDigestLength));
|
, _result(domain, _greases) {
|
||||||
|
_result.writeBlocks(rules.match([&](const MTPDtlsClientHello &data) {
|
||||||
|
return data.vblocks().v;
|
||||||
|
}));
|
||||||
|
_result.finalize(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClientHello Generator::take() {
|
||||||
|
auto digest = _result.extractDigest();
|
||||||
|
return { _result.take(), std::move(digest) };
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] ClientHello PrepareClientHello(
|
[[nodiscard]] ClientHello PrepareClientHello(
|
||||||
const MTPTlsClientHello &rules,
|
const MTPTlsClientHello &rules,
|
||||||
bytes::const_span domain,
|
bytes::const_span domain,
|
||||||
bytes::const_span key) {
|
bytes::const_span key) {
|
||||||
return ClientHelloGenerator(rules, domain, key).result();
|
return Generator(rules, domain, key).take();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] bool CheckPart(bytes::const_span data, QLatin1String check) {
|
[[nodiscard]] bool CheckPart(bytes::const_span data, QLatin1String check) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue