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:
parent
fdab2e61da
commit
12fd5be372
|
@ -44,7 +44,6 @@ set(sources
|
|||
policy
|
||||
puff
|
||||
random
|
||||
rsa
|
||||
rss
|
||||
session
|
||||
session_impl
|
||||
|
|
|
@ -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@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:: <
|
|||
{
|
||||
"a":
|
||||
{
|
||||
"cas": <em><optional 20 byte hash (string)></em>,
|
||||
"id": <em><20 byte id of sending node (string)></em>,
|
||||
"k": <em><ed25519 public key (32 bytes string)></em>,
|
||||
"seq": <em><monotonically increasing sequence number (integer)></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>
|
|||
"y": "r",
|
||||
}
|
||||
</pre>
|
||||
<p>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 <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">
|
||||
{
|
||||
"e": [ <em><error-code (integer)></em>, <em><error-string (string)></em> ],
|
||||
"t": <em><transaction-id (string)></em>,
|
||||
"y": "e",
|
||||
}
|
||||
</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 "3:seqi" <tt class="docutils literal">seq</tt> "e1:v" and the encoded value.
|
||||
sequence number 1 of value "Hello World!" would be converted to: 3:seqi1e1:v12:Hello World!
|
||||
<li>concatenate <em>"3:seqi"</em> <tt class="docutils literal">seq</tt> <em>"e1:v"</em> <tt class="docutils literal">len</tt> <em>":"</em> 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.</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>
|
||||
|
|
|
@ -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
|
||||
------------
|
||||
|
|
|
@ -84,7 +84,6 @@ nobase_include_HEADERS = \
|
|||
ptime.hpp \
|
||||
puff.hpp \
|
||||
random.hpp \
|
||||
rsa.hpp \
|
||||
rss.hpp \
|
||||
session.hpp \
|
||||
session_settings.hpp \
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
|
@ -86,7 +86,6 @@ libtorrent_rasterbar_la_SOURCES = \
|
|||
policy.cpp \
|
||||
puff.cpp \
|
||||
random.cpp \
|
||||
rsa.cpp \
|
||||
rss.cpp \
|
||||
session.cpp \
|
||||
session_impl.cpp \
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
148
src/rsa.cpp
148
src/rsa.cpp
|
@ -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
|
||||
|
||||
|
|
@ -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
|
||||
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue