Coverage Report

Created: 2025-07-18 06:51

/src/libtorrent/src/pe_crypto.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
3
Copyright (c) 2007, Un Shyam
4
Copyright (c) 2011, 2014-2019, Arvid Norberg
5
Copyright (c) 2016-2018, Alden Torres
6
Copyright (c) 2016, Andrei Kurushin
7
Copyright (c) 2016, 2018, Steven Siloti
8
All rights reserved.
9
10
Redistribution and use in source and binary forms, with or without
11
modification, are permitted provided that the following conditions
12
are met:
13
14
    * Redistributions of source code must retain the above copyright
15
      notice, this list of conditions and the following disclaimer.
16
    * Redistributions in binary form must reproduce the above copyright
17
      notice, this list of conditions and the following disclaimer in
18
      the documentation and/or other materials provided with the distribution.
19
    * Neither the name of the author nor the names of its
20
      contributors may be used to endorse or promote products derived
21
      from this software without specific prior written permission.
22
23
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33
POSSIBILITY OF SUCH DAMAGE.
34
35
*/
36
37
#if !defined TORRENT_DISABLE_ENCRYPTION
38
39
#include <cstdint>
40
#include <algorithm>
41
#include <random>
42
43
#include "libtorrent/aux_/disable_warnings_push.hpp"
44
45
#include <boost/multiprecision/integer.hpp>
46
#include <boost/multiprecision/cpp_int.hpp>
47
48
#include "libtorrent/aux_/disable_warnings_pop.hpp"
49
50
#include "libtorrent/random.hpp"
51
#include "libtorrent/aux_/alloca.hpp"
52
#include "libtorrent/pe_crypto.hpp"
53
#include "libtorrent/hasher.hpp"
54
55
namespace libtorrent {
56
57
  namespace mp = boost::multiprecision;
58
59
  namespace {
60
    // TODO: it would be nice to get the literal working
61
    key_t const dh_prime
62
      ("0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A63A36210000000000090563");
63
  }
64
65
  std::array<char, 96> export_key(key_t const& k)
66
0
  {
67
0
    std::array<char, 96> ret;
68
0
    auto* begin = reinterpret_cast<std::uint8_t*>(ret.data());
69
0
    std::uint8_t* end = mp::export_bits(k, begin, 8);
70
71
    // TODO: it would be nice to be able to export to a fixed width field, so
72
    // we wouldn't have to shift it later
73
0
    if (end < begin + 96)
74
0
    {
75
0
      int const len = int(end - begin);
76
#if defined __GNUC__ && __GNUC__ == 12
77
#pragma GCC diagnostic push
78
#pragma GCC diagnostic ignored "-Wstringop-overflow"
79
#endif
80
0
      std::memmove(begin + 96 - len, begin, aux::numeric_cast<std::size_t>(len));
81
#if defined __GNUC__ && __GNUC__ == 12
82
#pragma GCC diagnostic pop
83
#endif
84
0
      std::memset(begin, 0, aux::numeric_cast<std::size_t>(96 - len));
85
0
    }
86
0
    return ret;
87
0
  }
88
89
  void rc4_init(const unsigned char* in, std::size_t len, rc4 *state);
90
  std::size_t rc4_encrypt(unsigned char *out, std::size_t outlen, rc4 *state);
91
92
  // Set the prime P and the generator, generate local public key
93
  dh_key_exchange::dh_key_exchange()
94
0
  {
95
0
    aux::array<std::uint8_t, 96> random_key;
96
0
    aux::random_bytes({reinterpret_cast<char*>(random_key.data())
97
0
      , static_cast<std::ptrdiff_t>(random_key.size())});
98
99
    // create local key (random)
100
0
    mp::import_bits(m_dh_local_secret, random_key.begin(), random_key.end());
101
102
    // key = (2 ^ secret) % prime
103
0
    m_dh_local_key = mp::powm(key_t(2), m_dh_local_secret, dh_prime);
104
0
  }
105
106
  // compute shared secret given remote public key
107
  void dh_key_exchange::compute_secret(std::uint8_t const* remote_pubkey)
108
0
  {
109
0
    TORRENT_ASSERT(remote_pubkey);
110
0
    key_t key;
111
0
    mp::import_bits(key, remote_pubkey, remote_pubkey + 96);
112
0
    compute_secret(key);
113
0
  }
114
115
  void dh_key_exchange::compute_secret(key_t const& remote_pubkey)
116
0
  {
117
    // shared_secret = (remote_pubkey ^ local_secret) % prime
118
0
    m_dh_shared_secret = mp::powm(remote_pubkey, m_dh_local_secret, dh_prime);
119
120
0
    std::array<char, 96> buffer;
121
0
    mp::export_bits(m_dh_shared_secret, reinterpret_cast<std::uint8_t*>(buffer.data()), 8);
122
123
0
    static char const req3[4] = {'r', 'e', 'q', '3'};
124
    // calculate the xor mask for the obfuscated hash
125
0
    m_xor_mask = hasher(req3).update(buffer).final();
126
0
  }
127
128
  std::tuple<int, span<span<char const>>>
129
  encryption_handler::encrypt(
130
    span<span<char>> iovec)
131
0
  {
132
0
    TORRENT_ASSERT(!m_send_barriers.empty());
133
0
    TORRENT_ASSERT(m_send_barriers.front().enc_handler);
134
135
0
    int to_process = m_send_barriers.front().next;
136
137
0
    span<span<char>> bufs;
138
0
    bool need_destruct = false;
139
0
    if (to_process != INT_MAX)
140
0
    {
141
0
      TORRENT_ALLOCA(abufs, span<char>, iovec.size());
142
0
      bufs = abufs;
143
0
      need_destruct = true;
144
0
      int num_bufs = 0;
145
0
      for (int i = 0; to_process > 0 && i < iovec.size(); ++i)
146
0
      {
147
0
        ++num_bufs;
148
0
        int const size = int(iovec[i].size());
149
0
        if (to_process < size)
150
0
        {
151
0
          new (&bufs[i]) span<char>(
152
0
            iovec[i].data(), to_process);
153
0
          to_process = 0;
154
0
        }
155
0
        else
156
0
        {
157
0
          new (&bufs[i]) span<char>(iovec[i]);
158
0
          to_process -= size;
159
0
        }
160
0
      }
161
0
      bufs = bufs.first(num_bufs);
162
0
    }
163
0
    else
164
0
    {
165
0
      bufs = iovec;
166
0
    }
167
168
0
    int next_barrier = 0;
169
0
    span<span<char const>> out_iovec;
170
0
    if (!bufs.empty())
171
0
    {
172
0
      std::tie(next_barrier, out_iovec)
173
0
        = m_send_barriers.front().enc_handler->encrypt(bufs);
174
0
    }
175
176
0
    if (m_send_barriers.front().next != INT_MAX)
177
0
    {
178
      // to_process holds the difference between the size of the buffers
179
      // and the bytes left to the next barrier
180
      // if it's zero then pop the barrier
181
      // otherwise update the number of bytes remaining to the next barrier
182
0
      if (to_process == 0)
183
0
      {
184
0
        if (m_send_barriers.size() == 1)
185
0
        {
186
          // transitioning back to plaintext
187
0
          next_barrier = INT_MAX;
188
0
        }
189
0
        m_send_barriers.pop_front();
190
0
      }
191
0
      else
192
0
      {
193
0
        m_send_barriers.front().next = to_process;
194
0
      }
195
0
    }
196
197
0
#if TORRENT_USE_ASSERTS
198
0
    if (next_barrier != INT_MAX && next_barrier != 0)
199
0
    {
200
0
      int payload = 0;
201
0
      for (auto buf : bufs)
202
0
        payload += int(buf.size());
203
204
0
      int overhead = 0;
205
0
      for (auto buf : out_iovec)
206
0
        overhead += int(buf.size());
207
0
      TORRENT_ASSERT(overhead + payload == next_barrier);
208
0
    }
209
0
#endif
210
0
    if (need_destruct)
211
0
    {
212
0
      for (auto buf : bufs)
213
0
        buf.~span<char>();
214
0
    }
215
0
    return std::make_tuple(next_barrier, out_iovec);
216
0
  }
217
218
  int encryption_handler::decrypt(aux::crypto_receive_buffer& recv_buffer
219
    , std::size_t& bytes_transferred)
220
0
  {
221
0
    TORRENT_ASSERT(!is_recv_plaintext());
222
0
    int consume = 0;
223
0
    if (recv_buffer.crypto_packet_finished())
224
0
    {
225
0
      span<char> wr_buf = recv_buffer.mutable_buffer(int(bytes_transferred));
226
0
      int produce = 0;
227
0
      int packet_size = 0;
228
0
      std::tie(consume, produce, packet_size) = m_dec_handler->decrypt(wr_buf);
229
0
      TORRENT_ASSERT(packet_size || produce);
230
0
      TORRENT_ASSERT(packet_size >= 0);
231
0
      TORRENT_ASSERT(produce >= 0);
232
0
      bytes_transferred = std::size_t(produce);
233
0
      if (packet_size)
234
0
        recv_buffer.crypto_cut(consume, packet_size);
235
0
    }
236
0
    else
237
0
      bytes_transferred = 0;
238
0
    return consume;
239
0
  }
240
241
  bool encryption_handler::switch_send_crypto(std::shared_ptr<crypto_plugin> crypto
242
    , int pending_encryption)
243
0
  {
244
0
    bool place_barrier = false;
245
0
    if (!m_send_barriers.empty())
246
0
    {
247
0
      auto const end = std::prev(m_send_barriers.end());
248
0
      for (auto b = m_send_barriers.begin(); b != end; ++b)
249
0
        pending_encryption -= b->next;
250
0
      TORRENT_ASSERT(pending_encryption >= 0);
251
0
      m_send_barriers.back().next = pending_encryption;
252
0
    }
253
0
    else if (crypto)
254
0
      place_barrier = true;
255
256
0
    if (crypto)
257
0
      m_send_barriers.push_back(barrier(crypto, INT_MAX));
258
259
0
    return place_barrier;
260
0
  }
261
262
  void encryption_handler::switch_recv_crypto(std::shared_ptr<crypto_plugin> crypto
263
    , aux::crypto_receive_buffer& recv_buffer)
264
0
  {
265
0
    m_dec_handler = crypto;
266
0
    int packet_size = 0;
267
0
    if (crypto)
268
0
    {
269
0
      int consume = 0;
270
0
      int produce = 0;
271
0
      std::vector<span<char>> wr_buf;
272
0
      std::tie(consume, produce, packet_size) = crypto->decrypt(wr_buf);
273
0
      TORRENT_ASSERT(wr_buf.empty());
274
0
      TORRENT_ASSERT(consume == 0);
275
0
      TORRENT_ASSERT(produce == 0);
276
0
    }
277
0
    recv_buffer.crypto_reset(packet_size);
278
0
  }
279
280
  rc4_handler::rc4_handler()
281
0
    : m_encrypt(false)
282
0
    , m_decrypt(false)
283
0
  {
284
0
    m_rc4_incoming.x = 0;
285
0
    m_rc4_incoming.y = 0;
286
0
    m_rc4_outgoing.x = 0;
287
0
    m_rc4_outgoing.y = 0;
288
0
  }
289
290
  void rc4_handler::set_incoming_key(span<char const> key)
291
0
  {
292
0
    m_decrypt = true;
293
0
    rc4_init(reinterpret_cast<unsigned char const*>(key.data())
294
0
      , std::size_t(key.size()), &m_rc4_incoming);
295
    // Discard first 1024 bytes
296
0
    char buf[1024];
297
0
    span<char> vec(buf, sizeof(buf));
298
0
    decrypt(vec);
299
0
  }
300
301
  void rc4_handler::set_outgoing_key(span<char const> key)
302
0
  {
303
0
    m_encrypt = true;
304
0
    rc4_init(reinterpret_cast<unsigned char const*>(key.data())
305
0
      , std::size_t(key.size()), &m_rc4_outgoing);
306
    // Discard first 1024 bytes
307
0
    char buf[1024];
308
0
    span<char> vec(buf, sizeof(buf));
309
0
    encrypt(vec);
310
0
  }
311
312
  std::tuple<int, span<span<char const>>>
313
  rc4_handler::encrypt(span<span<char>> bufs)
314
0
  {
315
0
    span<span<char const>> empty;
316
0
    if (!m_encrypt) return std::make_tuple(0, empty);
317
0
    if (bufs.empty()) return std::make_tuple(0, empty);
318
319
0
    int bytes_processed = 0;
320
0
    for (auto& buf : bufs)
321
0
    {
322
0
      auto* const pos = reinterpret_cast<unsigned char*>(buf.data());
323
0
      int const len = int(buf.size());
324
325
0
      TORRENT_ASSERT(len >= 0);
326
0
      TORRENT_ASSERT(pos);
327
328
0
      bytes_processed += len;
329
0
      rc4_encrypt(pos, std::uint32_t(len), &m_rc4_outgoing);
330
0
    }
331
0
    return std::make_tuple(bytes_processed, empty);
332
0
  }
333
334
  std::tuple<int, int, int> rc4_handler::decrypt(span<span<char>> bufs)
335
0
  {
336
0
    if (!m_decrypt) return std::make_tuple(0, 0, 0);
337
338
0
    int bytes_processed = 0;
339
0
    for (auto& buf : bufs)
340
0
    {
341
0
      auto* const pos = reinterpret_cast<unsigned char*>(buf.data());
342
0
      int const len = int(buf.size());
343
344
0
      TORRENT_ASSERT(len >= 0);
345
0
      TORRENT_ASSERT(pos);
346
347
0
      bytes_processed += len;
348
0
      rc4_encrypt(pos, std::uint32_t(len), &m_rc4_incoming);
349
0
    }
350
0
    return std::make_tuple(0, bytes_processed, 0);
351
0
  }
352
353
// All this code is based on libTomCrypt (http://www.libtomcrypt.com/)
354
// this library is public domain and has been specially
355
// tailored for libtorrent by Arvid Norberg
356
357
void rc4_init(const unsigned char* in, std::size_t len, rc4 *state)
358
0
{
359
0
  std::size_t const key_size = sizeof(state->buf);
360
0
  aux::array<std::uint8_t, key_size> key;
361
0
  std::uint8_t tmp, *s;
362
0
  int keylen, x, y, j;
363
364
0
  TORRENT_ASSERT(state != nullptr);
365
0
  TORRENT_ASSERT(len <= key_size);
366
0
  if (len > key_size) len = key_size;
367
368
0
  state->x = 0;
369
0
  while (len--) {
370
0
    state->buf[state->x++] = *in++;
371
0
  }
372
373
  /* extract the key */
374
0
  s = state->buf.data();
375
0
  std::memcpy(key.data(), s, key_size);
376
0
  keylen = state->x;
377
378
  /* make RC4 perm and shuffle */
379
0
  for (x = 0; x < int(key_size); ++x) {
380
0
    s[x] = x & 0xff;
381
0
  }
382
383
0
  for (j = x = y = 0; x < int(key_size); x++) {
384
0
    y = (y + state->buf[x] + key[j++]) & 255;
385
0
    if (j == keylen) {
386
0
      j = 0;
387
0
    }
388
0
    tmp = s[x]; s[x] = s[y]; s[y] = tmp;
389
0
  }
390
0
  state->x = 0;
391
0
  state->y = 0;
392
0
}
393
394
std::size_t rc4_encrypt(unsigned char *out, std::size_t outlen, rc4 *state)
395
0
{
396
0
  std::uint8_t x, y, *s, tmp;
397
0
  std::size_t n;
398
399
0
  TORRENT_ASSERT(out != nullptr);
400
0
  TORRENT_ASSERT(state != nullptr);
401
402
0
  n = outlen;
403
0
  x = state->x & 0xff;
404
0
  y = state->y & 0xff;
405
0
  s = state->buf.data();
406
0
  while (outlen--) {
407
0
    x = (x + 1) & 255;
408
0
    y = (y + s[x]) & 255;
409
0
    tmp = s[x]; s[x] = s[y]; s[y] = tmp;
410
0
    tmp = (s[x] + s[y]) & 255;
411
0
    *out++ ^= s[tmp];
412
0
  }
413
0
  state->x = x;
414
0
  state->y = y;
415
0
  return n;
416
0
}
417
418
} // namespace libtorrent
419
420
#endif // TORRENT_DISABLE_ENCRYPTION