fix bug in ed25519 DHT signature verification. removed RSA code as it's no longer used. add CAS feature (compare and swap) to DHT put command. update dht_store documentation

This commit is contained in:
Arvid Norberg 2013-09-03 00:45:48 +00:00
parent fdab2e61da
commit 12fd5be372
12 changed files with 268 additions and 241 deletions

View File

@ -44,7 +44,6 @@ set(sources
policy
puff
random
rsa
rss
session
session_impl

View File

@ -511,7 +511,6 @@ SOURCES =
policy
puff
random
rsa
rss
session
session_impl

View File

@ -3,7 +3,7 @@
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.11: http://docutils.sourceforge.net/" />
<meta name="generator" content="Docutils 0.10: http://docutils.sourceforge.net/" />
<title>BitTorrent extension for arbitrary DHT store</title>
<meta name="author" content="Arvid Norberg, arvid&#64;rasterbar.com" />
<link rel="stylesheet" type="text/css" href="../../css/base.css" />
@ -192,7 +192,7 @@ number to a lower one, only upgrade. The sequence number SHOULD not exceed <tt c
(i.e. <tt class="docutils literal">0x7fffffffffffffff</tt>. A client MAY reject any message with a sequence number
exceeding this.</p>
<p>The signature is a 64 byte ed25519 signature of the bencoded sequence
number and <tt class="docutils literal">v</tt> key. e.g. something like this:: <tt class="docutils literal">3:seqi4e1:v12:Hello world!</tt>.</p>
number concatenated with the <tt class="docutils literal">v</tt> key. e.g. something like this:: <tt class="docutils literal">3:seqi4e1:v12:Hello world!</tt>.</p>
<div class="section" id="id1">
<h2>put message</h2>
<p>Request:</p>
@ -200,6 +200,7 @@ number and <tt class="docutils literal">v</tt> key. e.g. something like this:: <
{
&quot;a&quot;:
{
&quot;cas&quot;: <em>&lt;optional 20 byte hash (string)&gt;</em>,
&quot;id&quot;: <em>&lt;20 byte id of sending node (string)&gt;</em>,
&quot;k&quot;: <em>&lt;ed25519 public key (32 bytes string)&gt;</em>,
&quot;seq&quot;: <em>&lt;monotonically increasing sequence number (integer)&gt;</em>,
@ -216,9 +217,18 @@ number and <tt class="docutils literal">v</tt> key. e.g. something like this:: <
to what's already stored on the node, MUST reject the request. If the sequence
number is equal, and the value is also the same, the node SHOULD reset its timeout
counter.</p>
<p>If the sequence number in the <tt class="docutils literal">put</tt> message is lower than the sequence number
associated with the currently stored value, the storing node MAY return an error
message with code 302 (see error codes below).</p>
<p>Note that this request does not contain a target hash. The target hash under
which this blob is stored is implied by the <tt class="docutils literal">k</tt> argument. The key is
the SHA-1 hash of the key (<tt class="docutils literal">k</tt>).</p>
<p>The <tt class="docutils literal">cas</tt> field is optional. If present it is interpreted of the sha-1 hash of
the sequence number and <tt class="docutils literal">v</tt> field that is expected to be replaced. The buffer
to hash is the same as the one signed when storing. <tt class="docutils literal">cas</tt> is short for <em>compare
and swap</em>, it has similar semantics as CAS CPU instructions. If specified as part
of the put command, and the current value stored under the public key differs from
the expected value, the store fails. The <tt class="docutils literal">cas</tt> field only applies to mutable puts.</p>
<p>Response:</p>
<pre class="literal-block">
{
@ -227,6 +237,52 @@ the SHA-1 hash of the key (<tt class="docutils literal">k</tt>).</p>
&quot;y&quot;: &quot;r&quot;,
}
</pre>
<p>If the store fails for any reason an error message is returned instead of the message
template above, i.e. one where &quot;y&quot; is &quot;e&quot; and &quot;e&quot; is a tuple of [error-code, message]).
Failures include where the <tt class="docutils literal">cas</tt> hash mismatches and the sequence number is outdated.</p>
<p>If no <tt class="docutils literal">cas</tt> field is included in the <tt class="docutils literal">put</tt> message, the value of the current <tt class="docutils literal">v</tt>
field should be disregarded when determining whether or not to save the item.</p>
<p>The error message (as specified by <a class="reference external" href="http://www.bitorrent.org/beps/bep0005.html">BEP5</a>) looks like this:</p>
<pre class="literal-block">
{
&quot;e&quot;: [ <em>&lt;error-code (integer)&gt;</em>, <em>&lt;error-string (string)&gt;</em> ],
&quot;t&quot;: <em>&lt;transaction-id (string)&gt;</em>,
&quot;y&quot;: &quot;e&quot;,
}
</pre>
<p>In addition to the error codes defined in <a class="reference external" href="http://www.bitorrent.org/beps/bep0005.html">BEP5</a>, this specification defines
some additional error codes.</p>
<table border="1" class="docutils">
<colgroup>
<col width="29%" />
<col width="71%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">error-code</th>
<th class="head">description</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>205</td>
<td>message (i.e. <tt class="docutils literal">v</tt> field)
too big.</td>
</tr>
<tr><td>206</td>
<td>invalid signature</td>
</tr>
<tr><td>301</td>
<td>the CAS hash mismatched,
re-read value and try
again.</td>
</tr>
<tr><td>302</td>
<td>sequence number less than
current.</td>
</tr>
</tbody>
</table>
<p>An implementation MUST emit 301 errors if the cas-hash mismatches. This is
a critical feature in synchronization of multiple agents sharing an immutable item.</p>
</div>
<div class="section" id="id2">
<h2>get message</h2>
@ -265,12 +321,13 @@ the SHA-1 hash of the key (<tt class="docutils literal">k</tt>).</p>
</div>
<div class="section" id="signature-verification">
<h1>signature verification</h1>
<p>The signature, private and public keys are in the format as produced by this derivative <a class="reference external" href="https://github.com/nightcracker/ed25519">ed25519 library</a>.</p>
<p>In order to make it maximally difficult to attack the bencoding parser, signing and verification of the
value and sequence number should be done as follows:</p>
<ol class="arabic simple">
<li>encode value and sequence number separately</li>
<li>concatenate &quot;3:seqi&quot; <tt class="docutils literal">seq</tt> &quot;e1:v&quot; and the encoded value.
sequence number 1 of value &quot;Hello World!&quot; would be converted to: 3:seqi1e1:v12:Hello World!
<li>concatenate <em>&quot;3:seqi&quot;</em> <tt class="docutils literal">seq</tt> <em>&quot;e1:v&quot;</em> <tt class="docutils literal">len</tt> <em>&quot;:&quot;</em> and the encoded value.
sequence number 1 of value &quot;Hello World!&quot; would be converted to: &quot;3:seqi1e1:v12:Hello World!&quot;
In this way it is not possible to convince a node that part of the length is actually part of the
sequence number even if the parser contains certain bugs. Furthermore it is not possible to have a
verification failure if a bencoding serializer alters the order of entries in the dictionary.</li>
@ -286,8 +343,8 @@ the response SHOULD be considered invalid.</p>
<h1>expiration</h1>
<p>Without re-announcement, these items MAY expire in 2 hours. In order
to keep items alive, they SHOULD be re-announced once an hour.</p>
<p>Subscriber nodes MAY help out in announcing items the are interested in to the DHT,
to keep them alive.</p>
<p>Any node that's interested in keeping a blob in the DHT alive may announce it. It would simply
repeat the signature for a mutable put without having the private key.</p>
</div>
<div class="section" id="test-vectors">
<h1>test vectors</h1>

View File

@ -168,6 +168,7 @@ Request:
{
"a":
{
"cas": *<optional 20 byte hash (string)>*,
"id": *<20 byte id of sending node (string)>*,
"k": *<ed25519 public key (32 bytes string)>*,
"seq": *<monotonically increasing sequence number (integer)>*,
@ -185,10 +186,21 @@ to what's already stored on the node, MUST reject the request. If the sequence
number is equal, and the value is also the same, the node SHOULD reset its timeout
counter.
If the sequence number in the ``put`` message is lower than the sequence number
associated with the currently stored value, the storing node MAY return an error
message with code 302 (see error codes below).
Note that this request does not contain a target hash. The target hash under
which this blob is stored is implied by the ``k`` argument. The key is
the SHA-1 hash of the key (``k``).
The ``cas`` field is optional. If present it is interpreted of the sha-1 hash of
the sequence number and ``v`` field that is expected to be replaced. The buffer
to hash is the same as the one signed when storing. ``cas`` is short for *compare
and swap*, it has similar semantics as CAS CPU instructions. If specified as part
of the put command, and the current value stored under the public key differs from
the expected value, the store fails. The ``cas`` field only applies to mutable puts.
Response:
.. parsed-literal::
@ -199,6 +211,47 @@ Response:
"y": "r",
}
If the store fails for any reason an error message is returned instead of the message
template above, i.e. one where "y" is "e" and "e" is a tuple of [error-code, message]).
Failures include where the ``cas`` hash mismatches and the sequence number is outdated.
If no ``cas`` field is included in the ``put`` message, the value of the current ``v``
field should be disregarded when determining whether or not to save the item.
The error message (as specified by BEP5_) looks like this:
.. _BEP5: http://www.bitorrent.org/beps/bep0005.html
.. parsed-literal::
{
"e": [ *<error-code (integer)>*, *<error-string (string)>* ],
"t": *<transaction-id (string)>*,
"y": "e",
}
In addition to the error codes defined in BEP5_, this specification defines
some additional error codes.
+------------+-----------------------------+
| error-code | description |
+============+=============================+
| 205 | message (i.e. ``v`` field) |
| | too big. |
+------------+-----------------------------+
| 206 | invalid signature |
+------------+-----------------------------+
| 301 | the CAS hash mismatched, |
| | re-read value and try |
| | again. |
+------------+-----------------------------+
| 302 | sequence number less than |
| | current. |
+------------+-----------------------------+
An implementation MUST emit 301 errors if the cas-hash mismatches. This is
a critical feature in synchronization of multiple agents sharing an immutable item.
get message
...........
@ -240,12 +293,16 @@ Response:
signature verification
----------------------
The signature, private and public keys are in the format as produced by this derivative `ed25519 library`_.
.. _`ed25519 library`: https://github.com/nightcracker/ed25519
In order to make it maximally difficult to attack the bencoding parser, signing and verification of the
value and sequence number should be done as follows:
1. encode value and sequence number separately
2. concatenate "3:seqi" ``seq`` "e1:v" and the encoded value.
sequence number 1 of value "Hello World!" would be converted to: 3:seqi1e1:v12:Hello World!
2. concatenate *"3:seqi"* ``seq`` *"e1:v"* ``len`` *":"* and the encoded value.
sequence number 1 of value "Hello World!" would be converted to: "3:seqi1e1:v12:Hello World!"
In this way it is not possible to convince a node that part of the length is actually part of the
sequence number even if the parser contains certain bugs. Furthermore it is not possible to have a
verification failure if a bencoding serializer alters the order of entries in the dictionary.
@ -264,8 +321,8 @@ expiration
Without re-announcement, these items MAY expire in 2 hours. In order
to keep items alive, they SHOULD be re-announced once an hour.
Subscriber nodes MAY help out in announcing items the are interested in to the DHT,
to keep them alive.
Any node that's interested in keeping a blob in the DHT alive may announce it. It would simply
repeat the signature for a mutable put without having the private key.
test vectors
------------

View File

@ -84,7 +84,6 @@ nobase_include_HEADERS = \
ptime.hpp \
puff.hpp \
random.hpp \
rsa.hpp \
rss.hpp \
session.hpp \
session_settings.hpp \

View File

@ -136,7 +136,7 @@ struct ed25519_public_key { char bytes[32]; };
struct dht_mutable_item : dht_immutable_item
{
char sig[64];
int seq;
boost::uint64_t seq;
ed25519_public_key key;
};

View File

@ -1,60 +0,0 @@
/*
Copyright (c) 2011-2012, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef TORRENT_SIGN_HPP_INCLUDED
#define TORRENT_SIGN_HPP_INCLUDED
#include "libtorrent/config.hpp"
#include "libtorrent/hasher.hpp"
namespace libtorrent
{
// both of these use SHA-1 as the message digest to be signed/verified
// returns the size of the resulting signature
TORRENT_EXTRA_EXPORT int sign_rsa(sha1_hash const& digest
, char const* private_key, int private_len
, char* signature, int sig_len);
// returns true if the signature is valid
TORRENT_EXTRA_EXPORT bool verify_rsa(sha1_hash const& digest
, char const* public_key, int public_len
, char const* signature, int sig_len);
// returns false if it fails, for instance if the key
// buffers are too small. public_len and private_len
// are in-out values, set to the actual sizes
TORRENT_EXTRA_EXPORT bool generate_rsa_keys(char* public_key, int* public_len
, char* private_key, int* private_len, int key_size);
}
#endif // TORRENT_SIGN_HPP_INCLUDED

View File

@ -86,7 +86,6 @@ libtorrent_rasterbar_la_SOURCES = \
policy.cpp \
puff.cpp \
random.cpp \
rsa.cpp \
rss.cpp \
session.cpp \
session_impl.cpp \

View File

@ -57,7 +57,7 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent { namespace dht
{
void incoming_error(entry& e, char const* msg);
void incoming_error(entry& e, char const* msg, int error_code = 203);
using detail::write_endpoint;
@ -592,11 +592,11 @@ bool verify_message(lazy_entry const* msg, key_desc_t const desc[], lazy_entry c
return true;
}
void incoming_error(entry& e, char const* msg)
void incoming_error(entry& e, char const* msg, int error_code)
{
e["y"] = "e";
entry::list_type& l = e["e"].list();
l.push_back(entry(203));
l.push_back(entry(error_code));
l.push_back(entry(msg));
}
@ -637,6 +637,9 @@ void node_impl::incoming_request(msg const& m, entry& e)
if (!verify_id(id, m.addr.address()))
reply["ip"] = address_to_bytes(m.addr.address());
// mirror back the other node's external port
reply["p"] = m.addr.port();
if (strcmp(query, "ping") == 0)
{
// we already have 't' and 'id' in the response
@ -810,11 +813,12 @@ void node_impl::incoming_request(msg const& m, entry& e)
// public key
{"k", lazy_entry::string_t, 32, key_desc_t::optional},
{"sig", lazy_entry::string_t, 64, key_desc_t::optional},
{"cas", lazy_entry::string_t, 20, key_desc_t::optional},
};
// attempt to parse the message
lazy_entry const* msg_keys[5];
if (!verify_message(arg_ent, msg_desc, msg_keys, 5, error_string, sizeof(error_string)))
lazy_entry const* msg_keys[6];
if (!verify_message(arg_ent, msg_desc, msg_keys, 6, error_string, sizeof(error_string)))
{
incoming_error(e, error_string);
return;
@ -825,9 +829,9 @@ void node_impl::incoming_request(msg const& m, entry& e)
// pointer and length to the whole entry
std::pair<char const*, int> buf = msg_keys[1]->data_section();
if (buf.second > 767 || buf.second <= 0)
if (buf.second > 1000 || buf.second <= 0)
{
incoming_error(e, "message too big");
incoming_error(e, "message too big", 205);
return;
}
@ -861,6 +865,8 @@ void node_impl::incoming_request(msg const& m, entry& e)
{
// delete the least important one (i.e. the one
// the fewest peers are announcing)
// TODO: 3 also take into account how far away from our node ID
// the item is
dht_immutable_table_t::iterator j = std::min_element(m_immutable_table.begin()
, m_immutable_table.end()
, boost::bind(&dht_immutable_item::num_announcers
@ -888,19 +894,19 @@ void node_impl::incoming_request(msg const& m, entry& e)
{
// mutable put, we must verify the signature
// generate the message digest by merging the sequence number and the
hasher digest;
char seq[20];
int len = snprintf(seq, sizeof(seq), "3:seqi%"PRId64"e1:v", msg_keys[2]->int_value());
digest.update(seq, len);
char seq[1020];
int len = snprintf(seq, sizeof(seq), "3:seqi%" PRId64 "e1:v", msg_keys[2]->int_value());
std::pair<char const*, int> buf = msg_keys[1]->data_section();
digest.update(buf.first, buf.second);
memcpy(seq + len, buf.first, buf.second);
len += buf.second;
// msg_keys[4] is the signature, msg_keys[3] is the public key
if (ed25519_verify((unsigned char const*)msg_keys[4]->string_ptr()
, (unsigned char const*)buf.first, buf.second
, (unsigned char const*)seq, len
, (unsigned char const*)msg_keys[3]->string_ptr()) != 0)
{
incoming_error(e, "invalid signature");
incoming_error(e, "invalid signature", 206);
return;
}
@ -908,6 +914,7 @@ void node_impl::incoming_request(msg const& m, entry& e)
dht_mutable_table_t::iterator i = m_mutable_table.find(target);
if (i == m_mutable_table.end())
{
// this is the case where we don't have an item in this slot
// make sure we don't add too many items
if (int(m_mutable_table.size()) >= m_settings.max_dht_items)
{
@ -937,11 +944,29 @@ void node_impl::incoming_request(msg const& m, entry& e)
}
else
{
// this is the case where we already
dht_mutable_item* item = &i->second;
// this is the "cas" field in the put message
// if it was specified, we MUST make sure the current value
// matches the expected value before replacing it
if (msg_keys[5])
{
int len = snprintf(seq, sizeof(seq), "3:seqi%" PRId64 "e1:v", item->seq);
memcpy(seq + len, item->value, item->size);
len += item->size;
sha1_hash h = hasher(seq, len).final();
if (h != sha1_hash(msg_keys[5]->string_ptr()))
{
incoming_error(e, "CAS hash failed", 301);
return;
}
}
if (item->seq > msg_keys[2]->int_value())
{
incoming_error(e, "old sequence number");
incoming_error(e, "old sequence number", 302);
return;
}

View File

@ -268,7 +268,7 @@ void rpc_manager::unreachable(udp::endpoint const& ep)
}
// defined in node.cpp
void incoming_error(entry& e, char const* msg);
void incoming_error(entry& e, char const* msg, int error_code = 203);
bool rpc_manager::incoming(msg const& m, node_id* id)
{

View File

@ -1,148 +0,0 @@
/*
Copyright (c) 2011-2012, Arvid Norberg, Magnus Jonsson
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
* Neither the name of the author nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#include "libtorrent/rsa.hpp"
#include "libtorrent/hasher.hpp"
#if defined TORRENT_USE_OPENSSL
extern "C"
{
#include <openssl/rsa.h>
#include <openssl/objects.h> // for NID_sha1
}
namespace libtorrent
{
// returns the size of the resulting signature
int sign_rsa(sha1_hash const& digest
, char const* private_key, int private_len
, char* signature, int sig_len)
{
// convert bytestring to internal representation
// of the private key
RSA* priv = 0;
unsigned char const* key = (unsigned char const*)private_key;
priv = d2i_RSAPrivateKey(&priv, &key, private_len);
if (priv == 0) return -1;
if (RSA_size(priv) > sig_len)
{
RSA_free(priv);
return -1;
}
RSA_sign(NID_sha1, &digest[0], 20, (unsigned char*)signature, (unsigned int*)&sig_len, priv);
RSA_free(priv);
return sig_len;
}
// returns true if the signature is valid
bool verify_rsa(sha1_hash const& digest
, char const* public_key, int public_len
, char const* signature, int sig_len)
{
// convert bytestring to internal representation
// of the public key
RSA* pub = 0;
unsigned char const* key = (unsigned char const*)public_key;
pub = d2i_RSAPublicKey(&pub, &key, public_len);
if (pub == 0) return false;
int ret = RSA_verify(NID_sha1, &digest[0], 20, (unsigned char*)signature, sig_len, pub);
RSA_free(pub);
return ret;
}
bool generate_rsa_keys(char* public_key, int* public_len
, char* private_key, int* private_len, int key_size)
{
RSA* keypair = RSA_generate_key(key_size, 3, 0, 0);
if (keypair == 0) return false;
bool ret = false;
unsigned char* pub = (unsigned char*)public_key;
unsigned char* priv = (unsigned char*)private_key;
if (RSA_size(keypair) > *public_len) goto getout;
if (RSA_size(keypair) > *private_len) goto getout;
*public_len = i2d_RSAPublicKey(keypair, &pub);
*private_len = i2d_RSAPrivateKey(keypair, &priv);
ret = true;
getout:
RSA_free(keypair);
return ret;
}
} // namespace libtorrent
#else
// just stub these out for now, since they're not used anywhere yet
namespace libtorrent
{
// returns the size of the resulting signature
int sign_rsa(sha1_hash const& digest
, char const* private_key, int private_len
, char* signature, int sig_len)
{
return 0;
}
// returns true if the signature is valid
bool verify_rsa(sha1_hash const& digest
, char const* public_key, int public_len
, char const* signature, int sig_len)
{
return false;
}
bool generate_rsa_keys(char* public_key, int* public_len
, char* private_key, int* private_len, int key_size)
{
return false;
}
} // namespace libtorrent
#endif

View File

@ -127,7 +127,7 @@ void send_dht_msg(node_impl& node, char const* msg, udp::endpoint const& ep
, char const* target = 0, entry const* value = 0
, bool scrape = false, bool seed = false
, std::string const key = std::string(), std::string const sig = std::string()
, int seq = -1)
, int seq = -1, char const* cas = 0)
{
// we're about to clear out the backing buffer
// for this lazy_entry, so we better clear it now
@ -149,6 +149,7 @@ void send_dht_msg(node_impl& node, char const* msg, udp::endpoint const& ep
if (scrape) a["scrape"] = 1;
if (seed) a["seed"] = 1;
if (seq >= 0) a["seq"] = seq;
if (cas) a["cas"] = std::string(cas, 20);
char msg_buf[1500];
int size = bencode(msg_buf, e);
#if defined TORRENT_DEBUG && TORRENT_USE_IOSTREAM
@ -333,7 +334,7 @@ int test_main()
// DHT should be running on port 48199 now
lazy_entry response;
lazy_entry const* parsed[5];
lazy_entry const* parsed[10];
char error_string[200];
bool ret;
@ -644,6 +645,105 @@ int test_main()
TEST_ERROR(error_string);
}
send_dht_msg(node, "get", source, &response, "10", 0
, 0, no, 0, (char*)&hasher((char*)public_key, 32).final()[0]
, 0, false, false, std::string(), std::string(), 64);
key_desc_t desc3[] =
{
{ "r", lazy_entry::dict_t, 0, key_desc_t::parse_children },
{ "id", lazy_entry::string_t, 20, 0},
{ "v", lazy_entry::none_t, 0, 0},
{ "seq", lazy_entry::int_t, 0, 0},
{ "sig", lazy_entry::string_t, 0, 0},
{ "ip", lazy_entry::string_t, 0, key_desc_t::optional | key_desc_t::last_child},
{ "y", lazy_entry::string_t, 1, 0},
};
ret = verify_message(&response, desc3, parsed, 7, error_string, sizeof(error_string));
if (ret == 0)
{
fprintf(stderr, "msg: %s\n", print_entry(response).c_str());
fprintf(stderr, " invalid get response: %s\n%s\n"
, error_string, print_entry(response).c_str());
TEST_ERROR(error_string);
}
else
{
char value[1020];
char* ptr = value;
int value_len = bencode(ptr, items[0].ent);
TEST_EQUAL(value_len, parsed[2]->data_section().second);
TEST_CHECK(memcmp(parsed[2]->data_section().first, value, value_len) == 0);
TEST_EQUAL(seq, parsed[3]->int_value());
}
// === test CAS put ===
// this is the hash that we expect to be there
sha1_hash cas = hasher(buffer, pos).final();
// increment sequence number
++seq;
pos = snprintf(buffer, sizeof(buffer), "3:seqi%de1:v", seq);
ptr = buffer + pos;
// put item 1
pos += bencode(ptr, items[1].ent);
ed25519_sign(signature, (unsigned char*)buffer, pos, private_key, public_key);
send_dht_msg(node, "put", source, &response, "10", 0
, 0, token, 0, 0, &items[1].ent, false, false
, std::string((char*)public_key, 32)
, std::string((char*)signature, 64), seq
, (char const*)&cas[0]);
ret = verify_message(&response, desc2, parsed, 1, error_string, sizeof(error_string));
if (ret)
{
fprintf(stderr, "put response: %s\n"
, print_entry(response).c_str());
TEST_EQUAL(parsed[0]->string_value(), "r");
}
else
{
fprintf(stderr, " invalid put response: %s\n%s\n"
, error_string, print_entry(response).c_str());
TEST_ERROR(error_string);
}
// put the same message again. This should fail because the
// CAS hash is outdated, it's not the hash of the value that's
// stored anymore
send_dht_msg(node, "put", source, &response, "10", 0
, 0, token, 0, 0, &items[1].ent, false, false
, std::string((char*)public_key, 32)
, std::string((char*)signature, 64), seq
, (char const*)&cas[0]);
key_desc_t desc4[] =
{
{ "e", lazy_entry::list_t, 2, 0 },
{ "y", lazy_entry::string_t, 1, 0},
};
ret = verify_message(&response, desc4, parsed, 2, error_string, sizeof(error_string));
if (ret)
{
fprintf(stderr, "put response: %s\n"
, print_entry(response).c_str());
TEST_EQUAL(parsed[1]->string_value(), "e");
// 301 is the error code for CAS hash mismatch
TEST_EQUAL(parsed[0]->list_int_value_at(0), 301);
}
else
{
fprintf(stderr, " invalid put response: %s\n%s\nExpected failure 301 (CAS hash mismatch)\n"
, error_string, print_entry(response).c_str());
TEST_ERROR(error_string);
}
// test routing table
{