use crc32 for node ID restriction scheme

This commit is contained in:
Arvid Norberg 2013-08-19 05:24:35 +00:00
parent 27adb1fc44
commit b431ef7836
3 changed files with 41 additions and 30 deletions

View File

@ -63,20 +63,26 @@ In order to avoid the number node IDs controlled to grow linearly by the number
of IPs, as well as allowing more than one node ID per external IP, the node of IPs, as well as allowing more than one node ID per external IP, the node
ID can be restricted at each class level of the IP. ID can be restricted at each class level of the IP.
Another important property of the restriction put on node IDs is that the
distribution of the IDs remoain uniform. This is why CRC32 was chosen
as the hash function. See `comparisons of hash functions`__.
__ http://blog.libtorrent.org/2012/12/dht-security/
The expression to calculate a valid ID prefix (from an IPv4 address) is:: The expression to calculate a valid ID prefix (from an IPv4 address) is::
sha1((ip & 0x01071f7f) .. r) crc32((ip & 0x01071f7f) .. r)
And for an IPv6 address (``ip`` is the high 64 bits of the address):: And for an IPv6 address (``ip`` is the high 64 bits of the address)::
sha1((ip & 0x000103070f1f3f7f) .. r) crc32((ip & 0x000103070f1f3f7f) .. r)
``r`` is a random number in the range [0, 7]. The resulting integer, ``r`` is a random number in the range [0, 7]. The resulting integer,
representing the masked IP address is supposed to be big-endian before representing the masked IP address is supposed to be big-endian before
hashed. The ".." means concatenation. hashed. The ".." means concatenation.
The details of implementing this is to evaluate the expression, store the The details of implementing this is to evaluate the expression, store the
result in a big endian 64 bit integer and hash those 8 bytes with SHA-1. result in a big endian 64 bit integer and hash those 8 bytes with CRC32.
The first 4 bytes of the node ID used in the DHT MUST match the first 4 The first 4 bytes of the node ID used in the DHT MUST match the first 4
bytes in the resulting hash. The last byte of the hash MUST match the bytes in the resulting hash. The last byte of the hash MUST match the
@ -98,13 +104,17 @@ Example code code for calculating a valid node ID::
for (int i = 0; i < num_octets; ++i) for (int i = 0; i < num_octets; ++i)
ip[i] &= mask[i]; ip[i] &= mask[i];
SHA_CTX ctx;
SHA1_Init(&ctx);
SHA1_Update(&ctx, (unsigned char*)ip, num_octets);
uint32_t rand = rand() & 0xff; uint32_t rand = rand() & 0xff;
uint8_t r = rand & 0x7; uint8_t r = rand & 0x7;
SHA1_Update(&ctx, (unsigned char*)&r, 1);
SHA1_Final(&ctx, node_id); uint32_t crc = crc32(0, NULL, 0);
crc = crc32(crc, ip, num_octets);
crc = crc32(crc, &r, 1);
node_id[0] = (crc >> 24) & 0xff;
node_id[1] = (crc >> 16) & 0xff;
node_id[2] = (crc >> 8) & 0xff;
node_id[3] = crc & 0xff;
for (int i = 4; i < 19; ++i) node_id[i] = std::rand(); for (int i = 4; i < 19; ++i) node_id[i] = std::rand();
node_id[19] = rand; node_id[19] = rand;
@ -114,11 +124,11 @@ test vectors:
IP rand example node ID IP rand example node ID
============ ===== ========================================== ============ ===== ==========================================
124.31.75.21 1 **f766f9f5** 0c5d6a4ec8a88e4c6ab4c28b95eee4 **01** 124.31.75.21 1 **1712f6c7** 0c5d6a4ec8a88e4c6ab4c28b95eee4 **01**
21.75.31.124 86 **7ee04779** 4e7a08645677bbd1cfe7d8f956d532 **56** 21.75.31.124 86 **946406c1** 4e7a08645677bbd1cfe7d8f956d532 **56**
65.23.51.170 22 **76a626ff** bc8f112a3d426c84764f8c2a1150e6 **16** 65.23.51.170 22 **fefd9220** bc8f112a3d426c84764f8c2a1150e6 **16**
84.124.73.14 65 **beb4e619** 1bb1fe518101ceef99462b947a01ff **41** 84.124.73.14 65 **af1546dd** 1bb1fe518101ceef99462b947a01ff **41**
43.213.53.83 90 **ace5613a** 5b7c4be0237986d5243b87aa6d5130 **5a** 43.213.53.83 90 **a9e920bf** 5b7c4be0237986d5243b87aa6d5130 **5a**
The bold parts of the node ID are the important parts. The rest are The bold parts of the node ID are the important parts. The rest are
random numbers. random numbers.

View File

@ -34,6 +34,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <algorithm> #include <algorithm>
#include <ctime> #include <ctime>
#include <boost/crc.hpp>
#include "libtorrent/kademlia/node_id.hpp" #include "libtorrent/kademlia/node_id.hpp"
#include "libtorrent/hasher.hpp" #include "libtorrent/hasher.hpp"
@ -129,11 +130,19 @@ node_id generate_id_impl(address const& ip_, boost::uint32_t r)
for (int i = 0; i < num_octets; ++i) for (int i = 0; i < num_octets; ++i)
ip[i] &= mask[i]; ip[i] &= mask[i];
hasher h;
h.update((char*)ip, num_octets);
boost::uint8_t rand = r & 0x7; boost::uint8_t rand = r & 0x7;
h.update((char*)&rand, 1);
node_id id = h.final(); boost::crc_32_type crc;
crc.process_block(ip, ip + num_octets);
crc.process_byte(rand);
boost::uint32_t c = crc.checksum();
node_id id;
id[0] = (c >> 24) & 0xff;
id[1] = (c >> 16) & 0xff;
id[2] = (c >> 8) & 0xff;
id[3] = c & 0xff;
for (int i = 4; i < 19; ++i) id[i] = random(); for (int i = 4; i < 19; ++i) id[i] = random();
id[19] = r; id[19] = r;

View File

@ -71,14 +71,6 @@ using namespace boost::tuples;
namespace libtorrent { namespace libtorrent {
TORRENT_EXPORT std::string sanitize_path(std::string const& p); TORRENT_EXPORT std::string sanitize_path(std::string const& p);
#ifndef TORRENT_DISABLE_DHT
// namespace dht
// {
// TORRENT_EXPORT libtorrent::dht::node_id generate_id_impl(
// address const& ip_, boost::uint32_t r);
// }
#endif
} }
sha1_hash to_hash(char const* s) sha1_hash to_hash(char const* s)
@ -1965,11 +1957,11 @@ int test_main()
boost::uint8_t prefixes[][4] = boost::uint8_t prefixes[][4] =
{ {
{0xf7, 0x66, 0xf9, 0xf5}, { 0x17, 0x12, 0xf6, 0xc7 },
{0x7e, 0xe0, 0x47, 0x79 }, { 0x94, 0x64, 0x06, 0xc1 },
{0x76, 0xa6, 0x26, 0xff }, { 0xfe, 0xfd, 0x92, 0x20 },
{0xbe, 0xb4, 0xe6, 0x19 }, { 0xaf, 0x15, 0x46, 0xdd },
{0xac, 0xe5, 0x61, 0x3a }, { 0xa9, 0xe9, 0x20, 0xbf }
}; };
for (int i = 0; i < 5; ++i) for (int i = 0; i < 5; ++i)