forked from premiere/premiere-libtorrent
merged DHT fix from RC_0_16
This commit is contained in:
parent
6612d1f88a
commit
f12e1c1a3f
|
@ -5,6 +5,7 @@
|
||||||
* fix uTP edge case where udp socket buffer fills up
|
* fix uTP edge case where udp socket buffer fills up
|
||||||
* fix nagle implementation in uTP
|
* fix nagle implementation in uTP
|
||||||
|
|
||||||
|
* fix support for storing arbitrary data in the DHT
|
||||||
* fixed bug in uTP packet circle buffer
|
* fixed bug in uTP packet circle buffer
|
||||||
* fix potential crash when using torrent_handle::add_piece
|
* fix potential crash when using torrent_handle::add_piece
|
||||||
* added missing add_torrent_alert to python binding
|
* added missing add_torrent_alert to python binding
|
||||||
|
|
|
@ -98,14 +98,19 @@ i.e. the node ID of the node sending the message, to maintain the structure of t
|
||||||
network.</p>
|
network.</p>
|
||||||
<p>The <tt class="docutils literal">token</tt> field also has the same semantics as the standard DHT message <tt class="docutils literal">get_peers</tt>
|
<p>The <tt class="docutils literal">token</tt> field also has the same semantics as the standard DHT message <tt class="docutils literal">get_peers</tt>
|
||||||
and <tt class="docutils literal">announce_peer</tt>, when requesting an item and to write an item respectively.</p>
|
and <tt class="docutils literal">announce_peer</tt>, when requesting an item and to write an item respectively.</p>
|
||||||
|
<p>The <tt class="docutils literal">k</tt> field is the PKCS#1 encoded 2048 bit RSA public key, which the signature
|
||||||
|
can be authenticated with. When looking up a mutable item, the <tt class="docutils literal">target</tt> field
|
||||||
|
MUST be the SHA-1 hash of this key.</p>
|
||||||
<p>The distinction between storing mutable and immutable items is the inclusion
|
<p>The distinction between storing mutable and immutable items is the inclusion
|
||||||
of a public key, a sequence number and signature (<tt class="docutils literal">k</tt>, <tt class="docutils literal">seq</tt> and <tt class="docutils literal">sig</tt>).
|
of a public key, a sequence number and signature (<tt class="docutils literal">k</tt>, <tt class="docutils literal">seq</tt> and <tt class="docutils literal">sig</tt>).</p>
|
||||||
The distinction betwewn retrieving a mutable and immutable item is the inclusion of
|
<p><tt class="docutils literal">get</tt> requests for mutable items and immutable items cannot be distinguished from
|
||||||
the public key spill-over (<tt class="docutils literal">k</tt>) in the <tt class="docutils literal">get</tt> request.</p>
|
eachother. An implementation can either store mutable and immutable items in the same
|
||||||
<p>The <tt class="docutils literal">v</tt> key is the <em>value</em> to be stored. It is allowed to be any bencoded type (list,
|
hash table internally, or in separate ones and potentially do two lookups for <tt class="docutils literal">get</tt>
|
||||||
|
requests.</p>
|
||||||
|
<p>The <tt class="docutils literal">v</tt> field is the <em>value</em> to be stored. It is allowed to be any bencoded type (list,
|
||||||
dict, string or integer). When it's being hashed (for verifying its signature or to calculate
|
dict, string or integer). When it's being hashed (for verifying its signature or to calculate
|
||||||
its key), its flattened, bencoded, form is used.</p>
|
its key), its flattened, bencoded, form is used.</p>
|
||||||
<p>Storing nodes are SHOULD reject <tt class="docutils literal">put</tt> requests where the bencoded form of <tt class="docutils literal">v</tt> is longer
|
<p>Storing nodes SHOULD reject <tt class="docutils literal">put</tt> requests where the bencoded form of <tt class="docutils literal">v</tt> is longer
|
||||||
than 767 bytes.</p>
|
than 767 bytes.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="immutable-items">
|
<div class="section" id="immutable-items">
|
||||||
|
@ -172,7 +177,8 @@ there is no need to authenticate the origin of them. This makes immutable items
|
||||||
<h1>mutable items</h1>
|
<h1>mutable items</h1>
|
||||||
<p>Mutable items can be updated, without changing their DHT keys. To authenticate
|
<p>Mutable items can be updated, without changing their DHT keys. To authenticate
|
||||||
that only the original publisher can update an item, it is signed by a private key
|
that only the original publisher can update an item, it is signed by a private key
|
||||||
generated by the original publisher.</p>
|
generated by the original publisher. The target ID mutable items are stored under
|
||||||
|
is the SHA-1 hash of the public key (as it appears in the <tt class="docutils literal">put</tt> message).</p>
|
||||||
<p>In order to avoid a malicious node to overwrite the list head with an old
|
<p>In order to avoid a malicious node to overwrite the list head with an old
|
||||||
version, the sequence number <tt class="docutils literal">seq</tt> must be monotonically increasing for each update,
|
version, the sequence number <tt class="docutils literal">seq</tt> must be monotonically increasing for each update,
|
||||||
and a node hosting the list node MUST not downgrade a list head from a higher sequence
|
and a node hosting the list node MUST not downgrade a list head from a higher sequence
|
||||||
|
@ -187,7 +193,7 @@ number and <tt class="docutils literal">v</tt> key. e.g. something like this:: <
|
||||||
"a":
|
"a":
|
||||||
{
|
{
|
||||||
"id": <em><20 byte id of sending node (string)></em>,
|
"id": <em><20 byte id of sending node (string)></em>,
|
||||||
"k": <em><RSA-2048 public key (268 bytes string)></em>,
|
"k": <em><RSA-2048 public key (PKCS#1 encoded)></em>,
|
||||||
"seq": <em><monotonically increasing sequence number (integer)></em>,
|
"seq": <em><monotonically increasing sequence number (integer)></em>,
|
||||||
"sig": <em><RSA-2048 signature (256 bytes string)></em>,
|
"sig": <em><RSA-2048 signature (256 bytes string)></em>,
|
||||||
"token": <em><write-token (string)></em>,
|
"token": <em><write-token (string)></em>,
|
||||||
|
@ -217,8 +223,7 @@ stored on the node, MUST reject the request.</p>
|
||||||
"a":
|
"a":
|
||||||
{
|
{
|
||||||
"id": <em><20 byte id of sending node (string)></em>,
|
"id": <em><20 byte id of sending node (string)></em>,
|
||||||
"target:" <em><first 20 bytes of public key (string)></em>,
|
"target:" <em><20 byte SHA-1 hash of public key (string)></em>
|
||||||
"k": <em><remaining 248 bytes of public key (string)></em>
|
|
||||||
},
|
},
|
||||||
"t": <em><transaction-id (string)></em>,
|
"t": <em><transaction-id (string)></em>,
|
||||||
"y": "q",
|
"y": "q",
|
||||||
|
@ -259,6 +264,11 @@ verification failure if a bencoding serializer alters the order of entries in th
|
||||||
<li>hash the concatenated string with SHA-1</li>
|
<li>hash the concatenated string with SHA-1</li>
|
||||||
<li>sign or verify the hash digest.</li>
|
<li>sign or verify the hash digest.</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
<p>On the storage node, the signature MUST be verified before accepting the store command. The data
|
||||||
|
MUST be stored under the SHA-1 hash of the public key (as it appears in the bencoded dict).</p>
|
||||||
|
<p>On the subscribing nodes, the key they get back from a <tt class="docutils literal">get</tt> request MUST be verified to hash
|
||||||
|
to the target ID the lookup was made for, as well as verifying the signature. If any of these fail,
|
||||||
|
the response SHOULD be considered invalid.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="expiration">
|
<div class="section" id="expiration">
|
||||||
<h1>expiration</h1>
|
<h1>expiration</h1>
|
||||||
|
|
|
@ -44,16 +44,23 @@ network.
|
||||||
The ``token`` field also has the same semantics as the standard DHT message ``get_peers``
|
The ``token`` field also has the same semantics as the standard DHT message ``get_peers``
|
||||||
and ``announce_peer``, when requesting an item and to write an item respectively.
|
and ``announce_peer``, when requesting an item and to write an item respectively.
|
||||||
|
|
||||||
|
The ``k`` field is the PKCS#1 encoded 2048 bit RSA public key, which the signature
|
||||||
|
can be authenticated with. When looking up a mutable item, the ``target`` field
|
||||||
|
MUST be the SHA-1 hash of this key.
|
||||||
|
|
||||||
The distinction between storing mutable and immutable items is the inclusion
|
The distinction between storing mutable and immutable items is the inclusion
|
||||||
of a public key, a sequence number and signature (``k``, ``seq`` and ``sig``).
|
of a public key, a sequence number and signature (``k``, ``seq`` and ``sig``).
|
||||||
The distinction betwewn retrieving a mutable and immutable item is the inclusion of
|
|
||||||
the public key spill-over (``k``) in the ``get`` request.
|
|
||||||
|
|
||||||
The ``v`` key is the *value* to be stored. It is allowed to be any bencoded type (list,
|
``get`` requests for mutable items and immutable items cannot be distinguished from
|
||||||
|
eachother. An implementation can either store mutable and immutable items in the same
|
||||||
|
hash table internally, or in separate ones and potentially do two lookups for ``get``
|
||||||
|
requests.
|
||||||
|
|
||||||
|
The ``v`` field is the *value* to be stored. It is allowed to be any bencoded type (list,
|
||||||
dict, string or integer). When it's being hashed (for verifying its signature or to calculate
|
dict, string or integer). When it's being hashed (for verifying its signature or to calculate
|
||||||
its key), its flattened, bencoded, form is used.
|
its key), its flattened, bencoded, form is used.
|
||||||
|
|
||||||
Storing nodes are SHOULD reject ``put`` requests where the bencoded form of ``v`` is longer
|
Storing nodes SHOULD reject ``put`` requests where the bencoded form of ``v`` is longer
|
||||||
than 767 bytes.
|
than 767 bytes.
|
||||||
|
|
||||||
immutable items
|
immutable items
|
||||||
|
@ -131,7 +138,8 @@ mutable items
|
||||||
|
|
||||||
Mutable items can be updated, without changing their DHT keys. To authenticate
|
Mutable items can be updated, without changing their DHT keys. To authenticate
|
||||||
that only the original publisher can update an item, it is signed by a private key
|
that only the original publisher can update an item, it is signed by a private key
|
||||||
generated by the original publisher.
|
generated by the original publisher. The target ID mutable items are stored under
|
||||||
|
is the SHA-1 hash of the public key (as it appears in the ``put`` message).
|
||||||
|
|
||||||
In order to avoid a malicious node to overwrite the list head with an old
|
In order to avoid a malicious node to overwrite the list head with an old
|
||||||
version, the sequence number ``seq`` must be monotonically increasing for each update,
|
version, the sequence number ``seq`` must be monotonically increasing for each update,
|
||||||
|
@ -152,7 +160,7 @@ Request:
|
||||||
"a":
|
"a":
|
||||||
{
|
{
|
||||||
"id": *<20 byte id of sending node (string)>*,
|
"id": *<20 byte id of sending node (string)>*,
|
||||||
"k": *<RSA-2048 public key (268 bytes string)>*,
|
"k": *<RSA-2048 public key (PKCS#1 encoded)>*,
|
||||||
"seq": *<monotonically increasing sequence number (integer)>*,
|
"seq": *<monotonically increasing sequence number (integer)>*,
|
||||||
"sig": *<RSA-2048 signature (256 bytes string)>*,
|
"sig": *<RSA-2048 signature (256 bytes string)>*,
|
||||||
"token": *<write-token (string)>*,
|
"token": *<write-token (string)>*,
|
||||||
|
@ -187,8 +195,7 @@ Request:
|
||||||
"a":
|
"a":
|
||||||
{
|
{
|
||||||
"id": *<20 byte id of sending node (string)>*,
|
"id": *<20 byte id of sending node (string)>*,
|
||||||
"target:" *<first 20 bytes of public key (string)>*,
|
"target:" *<20 byte SHA-1 hash of public key (string)>*
|
||||||
"k": *<remaining 248 bytes of public key (string)>*
|
|
||||||
},
|
},
|
||||||
"t": *<transaction-id (string)>*,
|
"t": *<transaction-id (string)>*,
|
||||||
"y": "q",
|
"y": "q",
|
||||||
|
@ -230,6 +237,13 @@ value and sequence number should be done as follows:
|
||||||
3. hash the concatenated string with SHA-1
|
3. hash the concatenated string with SHA-1
|
||||||
4. sign or verify the hash digest.
|
4. sign or verify the hash digest.
|
||||||
|
|
||||||
|
On the storage node, the signature MUST be verified before accepting the store command. The data
|
||||||
|
MUST be stored under the SHA-1 hash of the public key (as it appears in the bencoded dict).
|
||||||
|
|
||||||
|
On the subscribing nodes, the key they get back from a ``get`` request MUST be verified to hash
|
||||||
|
to the target ID the lookup was made for, as well as verifying the signature. If any of these fail,
|
||||||
|
the response SHOULD be considered invalid.
|
||||||
|
|
||||||
expiration
|
expiration
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|
|
@ -131,14 +131,15 @@ struct dht_immutable_item
|
||||||
int size;
|
int size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct rsa_key { char bytes[268]; };
|
||||||
|
|
||||||
struct dht_mutable_item : dht_immutable_item
|
struct dht_mutable_item : dht_immutable_item
|
||||||
{
|
{
|
||||||
char sig[256];
|
char sig[256];
|
||||||
int seq;
|
int seq;
|
||||||
|
rsa_key key;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct rsa_key { char bytes[268]; };
|
|
||||||
|
|
||||||
inline bool operator<(rsa_key const& lhs, rsa_key const& rhs)
|
inline bool operator<(rsa_key const& lhs, rsa_key const& rhs)
|
||||||
{
|
{
|
||||||
return memcmp(lhs.bytes, rhs.bytes, sizeof(lhs.bytes)) < 0;
|
return memcmp(lhs.bytes, rhs.bytes, sizeof(lhs.bytes)) < 0;
|
||||||
|
@ -184,7 +185,7 @@ class TORRENT_EXTRA_EXPORT node_impl : boost::noncopyable
|
||||||
{
|
{
|
||||||
typedef std::map<node_id, torrent_entry> table_t;
|
typedef std::map<node_id, torrent_entry> table_t;
|
||||||
typedef std::map<node_id, dht_immutable_item> dht_immutable_table_t;
|
typedef std::map<node_id, dht_immutable_item> dht_immutable_table_t;
|
||||||
typedef std::map<rsa_key, dht_mutable_item> dht_mutable_table_t;
|
typedef std::map<node_id, dht_mutable_item> dht_mutable_table_t;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
node_impl(alert_dispatcher* alert_disp, udp_socket_interface* sock
|
node_impl(alert_dispatcher* alert_disp, udp_socket_interface* sock
|
||||||
|
|
|
@ -891,8 +891,7 @@ void node_impl::incoming_request(msg const& m, entry& e)
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
rsa_key target;
|
sha1_hash target = hasher(msg_keys[3]->string_ptr(), msg_keys[3]->string_length()).final();
|
||||||
memcpy(target.bytes, msg_keys[3]->string_ptr(), sizeof(target.bytes));
|
|
||||||
dht_mutable_table_t::iterator i = m_mutable_table.find(target);
|
dht_mutable_table_t::iterator i = m_mutable_table.find(target);
|
||||||
if (i == m_mutable_table.end())
|
if (i == m_mutable_table.end())
|
||||||
{
|
{
|
||||||
|
@ -916,6 +915,7 @@ void node_impl::incoming_request(msg const& m, entry& e)
|
||||||
memcpy(to_add.sig, msg_keys[4]->string_ptr(), sizeof(to_add.sig));
|
memcpy(to_add.sig, msg_keys[4]->string_ptr(), sizeof(to_add.sig));
|
||||||
TORRENT_ASSERT(sizeof(to_add.sig) == msg_keys[4]->string_length());
|
TORRENT_ASSERT(sizeof(to_add.sig) == msg_keys[4]->string_length());
|
||||||
memcpy(to_add.value, buf.first, buf.second);
|
memcpy(to_add.value, buf.first, buf.second);
|
||||||
|
memcpy(&to_add.key, msg_keys[3]->string_ptr(), sizeof(to_add.key));
|
||||||
|
|
||||||
boost::tie(i, boost::tuples::ignore) = m_mutable_table.insert(
|
boost::tie(i, boost::tuples::ignore) = m_mutable_table.insert(
|
||||||
std::make_pair(target, to_add));
|
std::make_pair(target, to_add));
|
||||||
|
@ -967,14 +967,13 @@ void node_impl::incoming_request(msg const& m, entry& e)
|
||||||
{
|
{
|
||||||
key_desc_t msg_desc[] = {
|
key_desc_t msg_desc[] = {
|
||||||
{"target", lazy_entry::string_t, 20, 0},
|
{"target", lazy_entry::string_t, 20, 0},
|
||||||
{"k", lazy_entry::string_t, 268-20, key_desc_t::optional},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// k is not used for now
|
// k is not used for now
|
||||||
|
|
||||||
// attempt to parse the message
|
// attempt to parse the message
|
||||||
lazy_entry const* msg_keys[2];
|
lazy_entry const* msg_keys[1];
|
||||||
if (!verify_message(arg_ent, msg_desc, msg_keys, 2, error_string, sizeof(error_string)))
|
if (!verify_message(arg_ent, msg_desc, msg_keys, 1, error_string, sizeof(error_string)))
|
||||||
{
|
{
|
||||||
incoming_error(e, error_string);
|
incoming_error(e, error_string);
|
||||||
return;
|
return;
|
||||||
|
@ -993,27 +992,22 @@ void node_impl::incoming_request(msg const& m, entry& e)
|
||||||
m_table.find_node(target, n, 0);
|
m_table.find_node(target, n, 0);
|
||||||
write_nodes_entry(reply, n);
|
write_nodes_entry(reply, n);
|
||||||
|
|
||||||
if (msg_keys[1])
|
dht_immutable_table_t::iterator i = m_immutable_table.find(target);
|
||||||
|
if (i != m_immutable_table.end())
|
||||||
{
|
{
|
||||||
rsa_key key;
|
dht_immutable_item const& f = i->second;
|
||||||
memcpy(key.bytes, msg_keys[0]->string_ptr(), 20);
|
reply["v"] = bdecode(f.value, f.value + f.size);
|
||||||
memcpy(key.bytes + 20, msg_keys[1]->string_ptr(), 268-20);
|
}
|
||||||
dht_mutable_table_t::iterator i = m_mutable_table.find(key);
|
else
|
||||||
|
{
|
||||||
|
dht_mutable_table_t::iterator i = m_mutable_table.find(target);
|
||||||
if (i != m_mutable_table.end())
|
if (i != m_mutable_table.end())
|
||||||
{
|
{
|
||||||
dht_mutable_item const& f = i->second;
|
dht_mutable_item const& f = i->second;
|
||||||
reply["v"] = bdecode(f.value, f.value + f.size);
|
reply["v"] = bdecode(f.value, f.value + f.size);
|
||||||
reply["seq"] = f.seq;
|
reply["seq"] = f.seq;
|
||||||
reply["sig"] = std::string(f.sig, f.sig + 256);
|
reply["sig"] = std::string(f.sig, f.sig + 256);
|
||||||
}
|
reply["k"] = std::string(f.key.bytes, f.key.bytes + sizeof(f.key.bytes));
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dht_immutable_table_t::iterator i = m_immutable_table.find(target);
|
|
||||||
if (i != m_immutable_table.end())
|
|
||||||
{
|
|
||||||
dht_immutable_item const& f = i->second;
|
|
||||||
reply["v"] = bdecode(f.value, f.value + f.size);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue