librats 2.3.2 → 2.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/lib/index.d.ts +1 -0
  2. package/native-src/CMakeLists.txt +15 -0
  3. package/native-src/src/librats/bindings/rats.cpp +62 -4
  4. package/native-src/src/librats/bindings/rats.h +32 -1
  5. package/native-src/src/librats/bittorrent/client.cpp +221 -26
  6. package/native-src/src/librats/bittorrent/client.h +73 -6
  7. package/native-src/src/librats/bittorrent/mse.cpp +626 -0
  8. package/native-src/src/librats/bittorrent/mse.h +218 -0
  9. package/native-src/src/librats/bittorrent/peer_connection.cpp +187 -59
  10. package/native-src/src/librats/bittorrent/peer_connection.h +109 -17
  11. package/native-src/src/librats/bittorrent/peer_link.cpp +134 -0
  12. package/native-src/src/librats/bittorrent/peer_link.h +145 -0
  13. package/native-src/src/librats/bittorrent/peer_list.cpp +75 -12
  14. package/native-src/src/librats/bittorrent/peer_list.h +130 -18
  15. package/native-src/src/librats/bittorrent/torrent.cpp +54 -11
  16. package/native-src/src/librats/bittorrent/torrent.h +19 -3
  17. package/native-src/src/librats/bittorrent/types.h +27 -0
  18. package/native-src/src/librats/bittorrent/utp_manager.cpp +234 -0
  19. package/native-src/src/librats/bittorrent/utp_manager.h +131 -0
  20. package/native-src/src/librats/bittorrent/utp_packet.h +216 -0
  21. package/native-src/src/librats/bittorrent/utp_stream.cpp +903 -0
  22. package/native-src/src/librats/bittorrent/utp_stream.h +400 -0
  23. package/native-src/src/librats/core/socket.cpp +44 -8
  24. package/native-src/src/librats/core/socket.h +23 -1
  25. package/native-src/src/librats/subsystems/file_transfer.cpp +54 -20
  26. package/native-src/src/librats/subsystems/file_transfer.h +21 -3
  27. package/package.json +1 -1
  28. package/src/librats_node.cpp +6 -3
@@ -0,0 +1,626 @@
1
+ #include "librats/bittorrent/mse.h"
2
+ #include "librats/bittorrent/byte_io.h"
3
+ #include "librats/bittorrent/log.h"
4
+ #include "librats/crypto/sha1.h"
5
+
6
+ #include <algorithm>
7
+ #include <cstring>
8
+ #include <random>
9
+
10
+ namespace librats::bittorrent::mse {
11
+
12
+ namespace {
13
+
14
+ // ── Randomness ──────────────────────────────────────────────────────────────
15
+ //
16
+ // The DH exponent comes straight from random_device: it is drawn once per
17
+ // connection, so the cost is irrelevant and seeding a PRNG from a single 32-bit
18
+ // value to produce it would be strictly worse. The pads are cosmetic — their only
19
+ // job is to vary the message length — so a cheap engine is fine there.
20
+
21
+ void random_bytes(std::uint8_t* out, std::size_t len) {
22
+ std::random_device rd;
23
+ for (std::size_t i = 0; i < len; ++i) out[i] = std::uint8_t(rd() & 0xFF);
24
+ }
25
+
26
+ void pad_bytes(std::uint8_t* out, std::size_t len) {
27
+ static thread_local std::mt19937 gen(std::random_device{}());
28
+ std::uniform_int_distribution<int> dist(0, 255);
29
+ for (std::size_t i = 0; i < len; ++i) out[i] = std::uint8_t(dist(gen));
30
+ }
31
+
32
+ std::size_t random_pad_len() {
33
+ static thread_local std::mt19937 gen(std::random_device{}());
34
+ std::uniform_int_distribution<std::size_t> dist(0, kMaxPad - 1);
35
+ return dist(gen);
36
+ }
37
+
38
+ // ── 768-bit modular arithmetic ──────────────────────────────────────────────
39
+ //
40
+ // MSE fixes one group: generator 2 over the 768-bit prime below (RFC 2409 group
41
+ // 1). That is the whole reason this file carries a bignum at all — the project's
42
+ // own crypto is curve25519, which cannot do a plain DH over an arbitrary prime.
43
+ //
44
+ // The implementation is Montgomery multiplication (CIOS) over 24 32-bit limbs,
45
+ // little-endian. Montgomery is chosen because it needs no division at all: the
46
+ // only alternative, schoolbook long division for the modular reduction, is far
47
+ // more code and far easier to get subtly wrong.
48
+ //
49
+ // No attempt is made at constant time. MSE is obfuscation, not security: the
50
+ // shared secret protects nothing an attacker who can time us could not simply
51
+ // read off the wire anyway (the info-hash travels in the clear in step 1 of the
52
+ // plaintext handshake we would otherwise have sent).
53
+
54
+ constexpr int kLimbs = 24; // 24 * 32 = 768 bits
55
+ using Num = std::array<std::uint32_t, kLimbs>;
56
+
57
+ /// P, big-endian. Note the top 64 bits are all ones, so 2^767 < P < 2^768 —
58
+ /// relied on below when reducing 2^768 mod P with a single subtraction.
59
+ constexpr std::uint8_t kPrimeBE[kKeyLen] = {
60
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xC9,0x0F,0xDA,0xA2,0x21,0x68,0xC2,0x34,
61
+ 0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1, 0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,
62
+ 0x02,0x0B,0xBE,0xA6,0x3B,0x13,0x9B,0x22, 0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD,
63
+ 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B, 0x30,0x2B,0x0A,0x6D,0xF2,0x5F,0x14,0x37,
64
+ 0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45, 0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,
65
+ 0xF4,0x4C,0x42,0xE9,0xA6,0x3A,0x36,0x21, 0x00,0x00,0x00,0x00,0x00,0x09,0x05,0x63,
66
+ };
67
+
68
+ Num from_be(const std::uint8_t* be, std::size_t len) {
69
+ Num n{};
70
+ // Big-endian bytes fill limbs from the least significant end backwards.
71
+ for (std::size_t i = 0; i < len; ++i) {
72
+ const std::size_t rev = len - 1 - i; // 0 = least significant byte
73
+ n[rev / 4] |= std::uint32_t(be[i]) << ((rev % 4) * 8);
74
+ }
75
+ return n;
76
+ }
77
+
78
+ void to_be(const Num& n, std::uint8_t* out) {
79
+ for (std::size_t i = 0; i < kKeyLen; ++i) {
80
+ const std::size_t rev = kKeyLen - 1 - i;
81
+ out[i] = std::uint8_t((n[rev / 4] >> ((rev % 4) * 8)) & 0xFF);
82
+ }
83
+ }
84
+
85
+ int cmp(const Num& a, const Num& b) {
86
+ for (int i = kLimbs - 1; i >= 0; --i) {
87
+ if (a[i] != b[i]) return a[i] < b[i] ? -1 : 1;
88
+ }
89
+ return 0;
90
+ }
91
+
92
+ /// a += b, returning the carry out of the top limb.
93
+ std::uint32_t add_in_place(Num& a, const Num& b) {
94
+ std::uint64_t carry = 0;
95
+ for (int i = 0; i < kLimbs; ++i) {
96
+ const std::uint64_t s = std::uint64_t(a[i]) + b[i] + carry;
97
+ a[i] = std::uint32_t(s);
98
+ carry = s >> 32;
99
+ }
100
+ return std::uint32_t(carry);
101
+ }
102
+
103
+ /// a -= b, returning the borrow out of the top limb.
104
+ std::uint32_t sub_in_place(Num& a, const Num& b) {
105
+ std::uint64_t borrow = 0;
106
+ for (int i = 0; i < kLimbs; ++i) {
107
+ const std::uint64_t d = std::uint64_t(a[i]) - b[i] - borrow;
108
+ a[i] = std::uint32_t(d);
109
+ borrow = (d >> 32) & 1;
110
+ }
111
+ return std::uint32_t(borrow);
112
+ }
113
+
114
+ /// The group constants, built once: P in limb form, -P^-1 mod 2^32 for the
115
+ /// Montgomery reduction, and R^2 mod P for entering Montgomery form.
116
+ struct Group {
117
+ Num p{};
118
+ Num r2{}; // 2^1536 mod P
119
+ Num one_mont{};// 2^768 mod P, i.e. Montgomery form of 1
120
+ std::uint32_t n0inv = 0;
121
+
122
+ Group() {
123
+ p = from_be(kPrimeBE, kKeyLen);
124
+
125
+ // n0inv = -p[0]^-1 mod 2^32. Newton's iteration doubles the number of
126
+ // correct bits each round, so five rounds from a 1-bit seed cover 32.
127
+ std::uint32_t inv = 1;
128
+ for (int i = 0; i < 5; ++i) inv *= 2u - p[0] * inv;
129
+ n0inv = std::uint32_t(0) - inv;
130
+
131
+ // R mod P, where R = 2^768. P > 2^767 means R < 2P, so one subtraction
132
+ // does it: R - P, computed as (0 - P) in 768-bit wraparound arithmetic.
133
+ Num r_mod_p{};
134
+ sub_in_place(r_mod_p, p);
135
+ one_mont = r_mod_p;
136
+
137
+ // R^2 mod P = (R mod P) * 2^768 mod P — 768 modular doublings. Cheap, and
138
+ // it keeps the constant self-evident instead of a magic literal nobody can
139
+ // check. Each doubling: t += t, then one conditional subtract (2t < 2P, so
140
+ // one is always enough; a carry out of the top limb means 2t > P too).
141
+ r2 = r_mod_p;
142
+ for (int i = 0; i < 768; ++i) {
143
+ const std::uint32_t carry = add_in_place(r2, r2);
144
+ if (carry || cmp(r2, p) >= 0) sub_in_place(r2, p);
145
+ }
146
+ }
147
+ };
148
+
149
+ const Group& group() {
150
+ static const Group g;
151
+ return g;
152
+ }
153
+
154
+ /// out = a * b * R^-1 mod P (Montgomery product), CIOS form.
155
+ void mont_mul(const Num& a, const Num& b, Num& out) {
156
+ const Group& g = group();
157
+ std::uint32_t t[kLimbs + 2] = {0};
158
+
159
+ for (int i = 0; i < kLimbs; ++i) {
160
+ std::uint64_t carry = 0;
161
+ for (int j = 0; j < kLimbs; ++j) {
162
+ const std::uint64_t s = std::uint64_t(t[j]) + std::uint64_t(a[j]) * b[i] + carry;
163
+ t[j] = std::uint32_t(s);
164
+ carry = s >> 32;
165
+ }
166
+ std::uint64_t s = std::uint64_t(t[kLimbs]) + carry;
167
+ t[kLimbs] = std::uint32_t(s);
168
+ t[kLimbs + 1] = std::uint32_t(s >> 32);
169
+
170
+ const std::uint32_t m = std::uint32_t(std::uint64_t(t[0]) * g.n0inv);
171
+
172
+ // t += m * P, which zeroes t[0]; the result is shifted down one limb as it
173
+ // is written, which is the division by 2^32 the Montgomery step needs.
174
+ carry = (std::uint64_t(t[0]) + std::uint64_t(m) * g.p[0]) >> 32;
175
+ for (int j = 1; j < kLimbs; ++j) {
176
+ const std::uint64_t s2 = std::uint64_t(t[j]) + std::uint64_t(m) * g.p[j] + carry;
177
+ t[j - 1] = std::uint32_t(s2);
178
+ carry = s2 >> 32;
179
+ }
180
+ s = std::uint64_t(t[kLimbs]) + carry;
181
+ t[kLimbs - 1] = std::uint32_t(s);
182
+ t[kLimbs] = t[kLimbs + 1] + std::uint32_t(s >> 32);
183
+ }
184
+
185
+ for (int i = 0; i < kLimbs; ++i) out[i] = t[i];
186
+ if (t[kLimbs] != 0 || cmp(out, g.p) >= 0) sub_in_place(out, g.p);
187
+ }
188
+
189
+ /// out = base^exp mod P, with `exp` a big-endian byte string.
190
+ void mod_exp(const Num& base, const std::uint8_t* exp, std::size_t exp_len, Num& out) {
191
+ const Group& g = group();
192
+
193
+ Num base_mont{};
194
+ mont_mul(base, g.r2, base_mont); // into Montgomery form
195
+ Num acc = g.one_mont; // 1, in Montgomery form
196
+
197
+ bool started = false; // skip the leading zero bits
198
+ for (std::size_t i = 0; i < exp_len; ++i) {
199
+ for (int bit = 7; bit >= 0; --bit) {
200
+ if (started) {
201
+ Num sq{};
202
+ mont_mul(acc, acc, sq);
203
+ acc = sq;
204
+ }
205
+ if ((exp[i] >> bit) & 1) {
206
+ if (!started) { acc = base_mont; started = true; }
207
+ else {
208
+ Num prod{};
209
+ mont_mul(acc, base_mont, prod);
210
+ acc = prod;
211
+ }
212
+ }
213
+ }
214
+ }
215
+ if (!started) { out = g.one_mont; } // exponent was zero
216
+ else { out = acc; }
217
+
218
+ Num one{};
219
+ one[0] = 1;
220
+ Num plain{};
221
+ mont_mul(out, one, plain); // out of Montgomery form
222
+ out = plain;
223
+ }
224
+
225
+ // ── SHA-1 helpers ───────────────────────────────────────────────────────────
226
+
227
+ using Digest = std::array<std::uint8_t, 20>;
228
+
229
+ /// SHA-1 over a four-character tag followed by up to two more buffers — every
230
+ /// hash MSE defines has exactly this shape.
231
+ Digest tagged_hash(const char (&tag)[5], const std::uint8_t* a, std::size_t alen,
232
+ const std::uint8_t* b = nullptr, std::size_t blen = 0) {
233
+ librats::SHA1 h;
234
+ h.update(reinterpret_cast<const std::uint8_t*>(tag), 4);
235
+ if (a && alen) h.update(a, alen);
236
+ if (b && blen) h.update(b, blen);
237
+ return h.finalize_bytes();
238
+ }
239
+
240
+ Digest xor_digest(const Digest& a, const Digest& b) {
241
+ Digest out{};
242
+ for (std::size_t i = 0; i < out.size(); ++i) out[i] = std::uint8_t(a[i] ^ b[i]);
243
+ return out;
244
+ }
245
+
246
+ /// The one method we agree to run, given what the peer offers and what we allow.
247
+ /// RC4 wins when both are on the table: it is the point of the exercise, and the
248
+ /// obfuscated-header-only mode leaves the payload trivially recognisable.
249
+ std::uint32_t select_method(std::uint32_t provided, std::uint32_t allowed) {
250
+ const std::uint32_t common = provided & allowed;
251
+ if (common & kRc4) return kRc4;
252
+ if (common & kPlaintext) return kPlaintext;
253
+ return 0;
254
+ }
255
+
256
+ void append(Bytes& out, const std::uint8_t* data, std::size_t len) {
257
+ out.insert(out.end(), data, data + len);
258
+ }
259
+
260
+ } // namespace
261
+
262
+ // ── Free helpers ────────────────────────────────────────────────────────────
263
+
264
+ bool skey_matches(const std::uint8_t* obfuscated, const std::uint8_t* req3_hash,
265
+ const InfoHash& candidate) {
266
+ const Digest req2 = tagged_hash("req2", candidate.data(), candidate.size());
267
+ for (std::size_t i = 0; i < req2.size(); ++i)
268
+ if (std::uint8_t(obfuscated[i] ^ req3_hash[i]) != req2[i]) return false;
269
+ return true;
270
+ }
271
+
272
+ // ── Rc4Cipher ───────────────────────────────────────────────────────────────
273
+
274
+ void Rc4Cipher::init(const std::uint8_t* key, std::size_t key_len) {
275
+ for (int i = 0; i < 256; ++i) s_[i] = std::uint8_t(i);
276
+ std::uint8_t j = 0;
277
+ for (int i = 0; i < 256; ++i) {
278
+ j = std::uint8_t(j + s_[i] + key[std::size_t(i) % key_len]);
279
+ std::swap(s_[i], s_[j]);
280
+ }
281
+ i_ = j_ = 0;
282
+ ready_ = true;
283
+
284
+ // The spec discards the first 1024 keystream bytes — RC4's early output is
285
+ // measurably biased, and every implementation in the swarm does this, so it is
286
+ // part of the wire format whether or not one cares about the bias.
287
+ std::uint8_t scratch[1024] = {0};
288
+ process(scratch, sizeof(scratch));
289
+ }
290
+
291
+ void Rc4Cipher::process(std::uint8_t* data, std::size_t len) {
292
+ for (std::size_t k = 0; k < len; ++k) {
293
+ i_ = std::uint8_t(i_ + 1);
294
+ j_ = std::uint8_t(j_ + s_[i_]);
295
+ std::swap(s_[i_], s_[j_]);
296
+ data[k] = std::uint8_t(data[k] ^ s_[std::uint8_t(s_[i_] + s_[j_])]);
297
+ }
298
+ }
299
+
300
+ // ── DhKeyExchange ───────────────────────────────────────────────────────────
301
+
302
+ DhKeyExchange::DhKeyExchange() {
303
+ random_bytes(private_.data(), private_.size());
304
+ // A zero exponent would make the public key 1, which the peer must reject —
305
+ // and which we would then have produced ourselves. Astronomically unlikely,
306
+ // but the fix is one byte.
307
+ private_[0] |= 0x80;
308
+
309
+ Num base{};
310
+ base[0] = 2;
311
+ Num pub{};
312
+ mod_exp(base, private_.data(), private_.size(), pub);
313
+ to_be(pub, public_.data());
314
+ }
315
+
316
+ bool DhKeyExchange::compute_secret(const std::uint8_t* remote_public) {
317
+ const Num remote = from_be(remote_public, kKeyLen);
318
+ const Group& g = group();
319
+
320
+ Num two{};
321
+ two[0] = 2;
322
+ Num p_minus_1 = g.p;
323
+ Num one{};
324
+ one[0] = 1;
325
+ sub_in_place(p_minus_1, one);
326
+
327
+ // Reject anything outside [2, P-2]: those values collapse the shared secret
328
+ // into a subgroup of one or two elements, which anyone can predict.
329
+ if (cmp(remote, two) < 0 || cmp(remote, p_minus_1) >= 0) return false;
330
+
331
+ Num s{};
332
+ mod_exp(remote, private_.data(), private_.size(), s);
333
+ to_be(s, secret_.data());
334
+ return true;
335
+ }
336
+
337
+ // ── Handshake ───────────────────────────────────────────────────────────────
338
+
339
+ Handshake::Handshake(const InfoHash& info_hash, Bytes ia, std::uint32_t provide)
340
+ : initiator_(true)
341
+ , state_(State::ReadYb)
342
+ , info_hash_(info_hash)
343
+ , ia_(std::move(ia))
344
+ , crypto_mask_(provide ? provide : std::uint32_t(kBoth)) {
345
+ // Step 1 goes out immediately — there is nothing to wait for.
346
+ const std::size_t pad = random_pad_len();
347
+ out_.resize(kKeyLen + pad);
348
+ std::memcpy(out_.data(), dh_.public_key().data(), kKeyLen);
349
+ pad_bytes(out_.data() + kKeyLen, pad);
350
+ }
351
+
352
+ Handshake::Handshake(SkeyResolver resolver, std::uint32_t allowed)
353
+ : initiator_(false)
354
+ , state_(State::ReadYa)
355
+ , crypto_mask_(allowed ? allowed : std::uint32_t(kBoth))
356
+ , resolver_(std::move(resolver)) {}
357
+
358
+ Handshake::Status Handshake::fail(const std::string& why) {
359
+ state_ = State::Failed;
360
+ error_ = why;
361
+ return Status::Failed;
362
+ }
363
+
364
+ Bytes Handshake::take_output() {
365
+ Bytes out;
366
+ out.swap(out_);
367
+ return out;
368
+ }
369
+
370
+ ByteView Handshake::leftover() const {
371
+ return ByteView(buf_.data() + pos_, buf_.size() - pos_);
372
+ }
373
+
374
+ const std::uint8_t* Handshake::take_decrypted(std::size_t n) {
375
+ std::uint8_t* at = buf_.data() + pos_;
376
+ result_.recv_cipher.process(at, n);
377
+ pos_ += n;
378
+ return at;
379
+ }
380
+
381
+ std::size_t Handshake::find_sync(const std::uint8_t* pattern, std::size_t len, std::size_t limit) {
382
+ if (available() < len) {
383
+ // Not even one candidate window yet. Everything before the last len-1 bytes
384
+ // can never start a match, so it is already accounted for against `limit`.
385
+ return std::string::npos;
386
+ }
387
+ const std::uint8_t* begin = buf_.data() + pos_;
388
+ const std::uint8_t* end = buf_.data() + buf_.size();
389
+ const std::uint8_t* hit = std::search(begin, end, pattern, pattern + len);
390
+ if (hit != end) return std::size_t(hit - buf_.data());
391
+
392
+ // No hit: drop everything that can no longer begin a match, and count it
393
+ // against the pad budget. Past the budget the peer is not speaking MSE (or not
394
+ // to us), and holding the bytes forever would be a slow memory leak.
395
+ const std::size_t droppable = available() - (len - 1);
396
+ scanned_ += droppable;
397
+ pos_ += droppable;
398
+ if (scanned_ > limit) fail("MSE sync marker not found within the pad limit");
399
+ return std::string::npos;
400
+ }
401
+
402
+ Handshake::Status Handshake::consume(const std::uint8_t* data, std::size_t len) {
403
+ if (state_ == State::Failed) return Status::Failed;
404
+ if (state_ == State::Done) return Status::Done;
405
+
406
+ // Compact away what the state machine has already eaten before growing, so a
407
+ // long sync scan does not accumulate discarded pad.
408
+ if (pos_ > 0 && pos_ == buf_.size()) { buf_.clear(); pos_ = 0; }
409
+ buf_.insert(buf_.end(), data, data + len);
410
+
411
+ // Bound on what one handshake may buffer. The protocol itself caps every field
412
+ // (96 + 512 key/pad, 40 hashes, 512 pads, a 68-byte IA), so anything near this
413
+ // is a peer that will never complete.
414
+ constexpr std::size_t kMaxBuffered = 64 * 1024;
415
+ if (buf_.size() > kMaxBuffered) return fail("MSE handshake buffer overflow");
416
+
417
+ return advance();
418
+ }
419
+
420
+ void Handshake::derive_ciphers(const InfoHash& skey, bool outgoing) {
421
+ const std::uint8_t* s = dh_.secret().data();
422
+ // A encrypts with keyA and decrypts with keyB; B is the mirror image.
423
+ const Digest key_a = tagged_hash("keyA", s, kKeyLen, skey.data(), skey.size());
424
+ const Digest key_b = tagged_hash("keyB", s, kKeyLen, skey.data(), skey.size());
425
+ const Digest& send = outgoing ? key_a : key_b;
426
+ const Digest& recv = outgoing ? key_b : key_a;
427
+ result_.send_cipher.init(send.data(), send.size());
428
+ result_.recv_cipher.init(recv.data(), recv.size());
429
+ }
430
+
431
+ Handshake::Status Handshake::advance() {
432
+ for (;;) {
433
+ switch (state_) {
434
+
435
+ // ---- initiator ----
436
+
437
+ case State::ReadYb: {
438
+ if (available() < kKeyLen) return Status::NeedMore;
439
+ if (!dh_.compute_secret(buf_.data() + pos_))
440
+ return fail("peer sent a degenerate DH public key");
441
+ pos_ += kKeyLen;
442
+
443
+ derive_ciphers(info_hash_, /*outgoing=*/true);
444
+
445
+ // Step 3. The two hashes go out in the clear; everything from VC on is
446
+ // encrypted with the send cipher, which is then left positioned exactly
447
+ // where the payload stream picks up.
448
+ const std::uint8_t* s = dh_.secret().data();
449
+ const Digest sync = tagged_hash("req1", s, kKeyLen);
450
+ const Digest req2 = tagged_hash("req2", info_hash_.data(), info_hash_.size());
451
+ const Digest req3 = tagged_hash("req3", s, kKeyLen);
452
+ const Digest obfusc = xor_digest(req2, req3);
453
+
454
+ append(out_, sync.data(), sync.size());
455
+ append(out_, obfusc.data(), obfusc.size());
456
+
457
+ const std::size_t pad = random_pad_len();
458
+ Bytes enc;
459
+ enc.resize(kVcLen + 4 + 2 + pad + 2 + ia_.size());
460
+ std::uint8_t* p = enc.data();
461
+ std::memset(p, 0, kVcLen); p += kVcLen; // VC
462
+ write_u32_be(p, crypto_mask_); p += 4;
463
+ write_u16_be(p, std::uint16_t(pad)); p += 2;
464
+ pad_bytes(p, pad); p += pad;
465
+ write_u16_be(p, std::uint16_t(ia_.size())); p += 2;
466
+ if (!ia_.empty()) std::memcpy(p, ia_.data(), ia_.size());
467
+ result_.send_cipher.process(enc.data(), enc.size());
468
+ append(out_, enc.data(), enc.size());
469
+ ia_.clear();
470
+
471
+ // What the peer's encrypted VC will look like. Computed on a *copy* of
472
+ // the receive cipher so the real one stays at stream position zero —
473
+ // the VC bytes themselves still have to pass through it.
474
+ Rc4Cipher probe = result_.recv_cipher;
475
+ std::memset(expected_vc_.data(), 0, expected_vc_.size());
476
+ probe.process(expected_vc_.data(), expected_vc_.size());
477
+
478
+ scanned_ = 0;
479
+ state_ = State::SyncVc;
480
+ break;
481
+ }
482
+
483
+ case State::SyncVc: {
484
+ // PadB sits between Yb and step 4 and its length is never sent, so the
485
+ // encrypted VC is the only way to find where step 4 begins.
486
+ const std::size_t at = find_sync(expected_vc_.data(), expected_vc_.size(), kMaxPad);
487
+ if (state_ == State::Failed) return Status::Failed;
488
+ if (at == std::string::npos) return Status::NeedMore;
489
+ scanned_ += at - pos_;
490
+ pos_ = at; // pos_ now points at the encrypted VC
491
+ state_ = State::ReadVcBody;
492
+ break;
493
+ }
494
+
495
+ case State::ReadVcBody: {
496
+ constexpr std::size_t kBody = kVcLen + 4 + 2; // VC, crypto_select, len(PadD)
497
+ if (available() < kBody) return Status::NeedMore;
498
+ const std::uint8_t* p = take_decrypted(kBody);
499
+ // VC is guaranteed by the sync match, but a peer could have sent the
500
+ // pattern as pad; re-checking costs nothing and keeps the invariant local.
501
+ for (std::size_t i = 0; i < kVcLen; ++i)
502
+ if (p[i] != 0) return fail("MSE verification constant mismatch");
503
+
504
+ const std::uint32_t selected = read_u32_be(p + kVcLen);
505
+ if (selected != kRc4 && selected != kPlaintext)
506
+ return fail("peer selected an unknown crypto method");
507
+ if ((selected & crypto_mask_) == 0)
508
+ return fail("peer selected a crypto method we did not offer");
509
+ result_.rc4_payload = (selected == kRc4);
510
+
511
+ pad_len_ = read_u16_be(p + kVcLen + 4);
512
+ if (pad_len_ > kMaxPad) return fail("MSE PadD too long");
513
+ state_ = State::ReadPadD;
514
+ break;
515
+ }
516
+
517
+ case State::ReadPadD: {
518
+ if (available() < pad_len_) return Status::NeedMore;
519
+ take_decrypted(pad_len_); // discarded, but the cipher advances
520
+ state_ = State::Done;
521
+ return Status::Done;
522
+ }
523
+
524
+ // ---- receiver ----
525
+
526
+ case State::ReadYa: {
527
+ if (available() < kKeyLen) return Status::NeedMore;
528
+ if (!dh_.compute_secret(buf_.data() + pos_))
529
+ return fail("peer sent a degenerate DH public key");
530
+ pos_ += kKeyLen;
531
+
532
+ // Step 2 out immediately; the ciphers wait for SKEY in step 3.
533
+ const std::size_t pad = random_pad_len();
534
+ append(out_, dh_.public_key().data(), kKeyLen);
535
+ Bytes padding(pad);
536
+ if (pad) pad_bytes(padding.data(), pad);
537
+ append(out_, padding.data(), padding.size());
538
+
539
+ scanned_ = 0;
540
+ state_ = State::SyncHash;
541
+ break;
542
+ }
543
+
544
+ case State::SyncHash: {
545
+ const Digest sync = tagged_hash("req1", dh_.secret().data(), kKeyLen);
546
+ const std::size_t at = find_sync(sync.data(), sync.size(), kMaxPad);
547
+ if (state_ == State::Failed) return Status::Failed;
548
+ if (at == std::string::npos) return Status::NeedMore;
549
+ scanned_ += at - pos_;
550
+ pos_ = at + sync.size(); // skip PadA and the marker itself
551
+ state_ = State::ReadSkey;
552
+ break;
553
+ }
554
+
555
+ case State::ReadSkey: {
556
+ if (available() < 20) return Status::NeedMore;
557
+ const Digest req3 = tagged_hash("req3", dh_.secret().data(), kKeyLen);
558
+ InfoHash resolved{};
559
+ if (!resolver_ || !resolver_(buf_.data() + pos_, req3.data(), resolved))
560
+ return fail("MSE stream key names a torrent we do not have");
561
+ pos_ += 20;
562
+
563
+ info_hash_ = resolved;
564
+ result_.info_hash = resolved;
565
+ derive_ciphers(resolved, /*outgoing=*/false);
566
+ state_ = State::ReadVcCrypto;
567
+ break;
568
+ }
569
+
570
+ case State::ReadVcCrypto: {
571
+ constexpr std::size_t kBody = kVcLen + 4 + 2; // VC, crypto_provide, len(PadC)
572
+ if (available() < kBody) return Status::NeedMore;
573
+ const std::uint8_t* p = take_decrypted(kBody);
574
+ for (std::size_t i = 0; i < kVcLen; ++i)
575
+ if (p[i] != 0) return fail("MSE verification constant mismatch");
576
+
577
+ const std::uint32_t provided = read_u32_be(p + kVcLen);
578
+ const std::uint32_t selected = select_method(provided, crypto_mask_);
579
+ if (selected == 0) return fail("no crypto method in common with the peer");
580
+ result_.rc4_payload = (selected == kRc4);
581
+
582
+ pad_len_ = read_u16_be(p + kVcLen + 4);
583
+ if (pad_len_ > kMaxPad) return fail("MSE PadC too long");
584
+
585
+ // Step 4 can be built now: it depends only on the choice just made.
586
+ // Encrypting it here also leaves the send cipher exactly where the
587
+ // payload stream continues.
588
+ const std::size_t pad = random_pad_len();
589
+ Bytes enc;
590
+ enc.resize(kVcLen + 4 + 2 + pad);
591
+ std::uint8_t* q = enc.data();
592
+ std::memset(q, 0, kVcLen); q += kVcLen;
593
+ write_u32_be(q, selected); q += 4;
594
+ write_u16_be(q, std::uint16_t(pad)); q += 2;
595
+ pad_bytes(q, pad);
596
+ result_.send_cipher.process(enc.data(), enc.size());
597
+ append(out_, enc.data(), enc.size());
598
+
599
+ state_ = State::ReadPadC;
600
+ break;
601
+ }
602
+
603
+ case State::ReadPadC: {
604
+ if (available() < pad_len_ + 2) return Status::NeedMore;
605
+ const std::uint8_t* p = take_decrypted(pad_len_ + 2);
606
+ ia_len_ = read_u16_be(p + pad_len_);
607
+ if (ia_len_ > kMaxIa) return fail("MSE initial payload too long");
608
+ state_ = State::ReadIa;
609
+ break;
610
+ }
611
+
612
+ case State::ReadIa: {
613
+ if (available() < ia_len_) return Status::NeedMore;
614
+ const std::uint8_t* p = take_decrypted(ia_len_);
615
+ result_.initial_payload.assign(p, p + ia_len_);
616
+ state_ = State::Done;
617
+ return Status::Done;
618
+ }
619
+
620
+ case State::Done: return Status::Done;
621
+ case State::Failed: return Status::Failed;
622
+ }
623
+ }
624
+ }
625
+
626
+ } // namespace librats::bittorrent::mse