forked from premiere/premiere-libtorrent
support salt feature in DHT put
This commit is contained in:
parent
f26df6cbfa
commit
54bbd3cae0
|
@ -170,7 +170,7 @@ concatenated with the ``v`` key. e.g. something like this::
|
||||||
If the ``salt`` key is present and non-empty, the salt string must be included
|
If the ``salt`` key is present and non-empty, the salt string must be included
|
||||||
in what's signed. Note that if ``salt`` is specified and an empty string, it is
|
in what's signed. Note that if ``salt`` is specified and an empty string, it is
|
||||||
as if it was not specified and nothing in addition to the sequence number and
|
as if it was not specified and nothing in addition to the sequence number and
|
||||||
the data is signed.
|
the data is signed. The salt string may not be longer than 64 bytes.
|
||||||
|
|
||||||
When a salt is included in what is signed, the key ``salt`` with the value of
|
When a salt is included in what is signed, the key ``salt`` with the value of
|
||||||
the key is prepended in its bencoded form. For example, if ``salt`` is "foobar",
|
the key is prepended in its bencoded form. For example, if ``salt`` is "foobar",
|
||||||
|
@ -230,6 +230,12 @@ publisher doesn't know ahead of time how many different items are to be
|
||||||
published. It can distribute a single public key for users to authenticate the
|
published. It can distribute a single public key for users to authenticate the
|
||||||
published blobs.
|
published blobs.
|
||||||
|
|
||||||
|
Note that the salt is not returned in the response to a ``get`` request. This
|
||||||
|
is intentional. When issuing a ``get`` request for an item is expected to
|
||||||
|
know what the salt is (because it is part of what the target ID that is being
|
||||||
|
looked up is derived from). There is no need to repeat it back for bystanders
|
||||||
|
to see.
|
||||||
|
|
||||||
The ``cas`` field is optional. If present it is interpreted as the sha-1 hash of
|
The ``cas`` field is optional. If present it is interpreted as the sha-1 hash of
|
||||||
the sequence number, ``v`` field and possibly the ``salt`` field, that is
|
the sequence number, ``v`` field and possibly the ``salt`` field, that is
|
||||||
expected to be replaced. The buffer to hash is the same as the one signed when
|
expected to be replaced. The buffer to hash is the same as the one signed when
|
||||||
|
@ -282,6 +288,9 @@ some additional error codes.
|
||||||
+------------+-----------------------------+
|
+------------+-----------------------------+
|
||||||
| 206 | invalid signature |
|
| 206 | invalid signature |
|
||||||
+------------+-----------------------------+
|
+------------+-----------------------------+
|
||||||
|
| 207 | salt (i.e. ``salt`` field) |
|
||||||
|
| | too big. |
|
||||||
|
+------------+-----------------------------+
|
||||||
| 301 | the CAS hash mismatched, |
|
| 301 | the CAS hash mismatched, |
|
||||||
| | re-read value and try |
|
| | re-read value and try |
|
||||||
| | again. |
|
| | again. |
|
||||||
|
@ -323,7 +332,6 @@ Response:
|
||||||
"k": *<curve25519 public key (32 bytes string)>*,
|
"k": *<curve25519 public key (32 bytes string)>*,
|
||||||
"nodes": *<IPv4 nodes close to 'target'>*,
|
"nodes": *<IPv4 nodes close to 'target'>*,
|
||||||
"nodes6": *<IPv6 nodes close to 'target'>*,
|
"nodes6": *<IPv6 nodes close to 'target'>*,
|
||||||
"salt": *<optional salt to be appended to "k" when hashing (string)>*
|
|
||||||
"seq": *<monotonically increasing sequence number (integer)>*,
|
"seq": *<monotonically increasing sequence number (integer)>*,
|
||||||
"sig": *<curve25519 signature (64 bytes string)>*,
|
"sig": *<curve25519 signature (64 bytes string)>*,
|
||||||
"token": *<write-token (string)>*,
|
"token": *<write-token (string)>*,
|
||||||
|
@ -369,10 +377,17 @@ 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
|
It would simply repeat the signature for a mutable put without having the
|
||||||
private key.
|
private key.
|
||||||
|
|
||||||
test vector
|
test vectors
|
||||||
-----------
|
------------
|
||||||
|
|
||||||
The buffer being signed::
|
test 1 (mutable)
|
||||||
|
................
|
||||||
|
|
||||||
|
value::
|
||||||
|
|
||||||
|
12:Hello World!
|
||||||
|
|
||||||
|
buffer being signed::
|
||||||
|
|
||||||
3:seqi1e1:v12:Hello World!
|
3:seqi1e1:v12:Hello World!
|
||||||
|
|
||||||
|
@ -385,11 +400,59 @@ private key::
|
||||||
e06d3183d14159228433ed599221b80bd0a5ce8352e4bdf0262f76786ef1c74d
|
e06d3183d14159228433ed599221b80bd0a5ce8352e4bdf0262f76786ef1c74d
|
||||||
b7e7a9fea2c0eb269d61e3b38e450a22e754941ac78479d6c54e1faf6037881d
|
b7e7a9fea2c0eb269d61e3b38e450a22e754941ac78479d6c54e1faf6037881d
|
||||||
|
|
||||||
signature::
|
**target ID**::
|
||||||
|
|
||||||
|
4a533d47ec9c7d95b1ad75f576cffc641853b750
|
||||||
|
|
||||||
|
**signature**::
|
||||||
|
|
||||||
305ac8aeb6c9c151fa120f120ea2cfb923564e11552d06a5d856091e5e853cff
|
305ac8aeb6c9c151fa120f120ea2cfb923564e11552d06a5d856091e5e853cff
|
||||||
1260d3f39e4999684aa92eb73ffd136e6f4f3ecbfda0ce53a1608ecd7ae21f01
|
1260d3f39e4999684aa92eb73ffd136e6f4f3ecbfda0ce53a1608ecd7ae21f01
|
||||||
|
|
||||||
|
test 2 (mutable with salt)
|
||||||
|
..........................
|
||||||
|
|
||||||
|
value::
|
||||||
|
|
||||||
|
12:Hello World!
|
||||||
|
|
||||||
|
salt::
|
||||||
|
|
||||||
|
foobar
|
||||||
|
|
||||||
|
buffer being signed::
|
||||||
|
|
||||||
|
4:salt6:foobar3:seqi1e1:v12:Hello World!
|
||||||
|
|
||||||
|
public key::
|
||||||
|
|
||||||
|
77ff84905a91936367c01360803104f92432fcd904a43511876df5cdf3e7e548
|
||||||
|
|
||||||
|
private key::
|
||||||
|
|
||||||
|
e06d3183d14159228433ed599221b80bd0a5ce8352e4bdf0262f76786ef1c74d
|
||||||
|
b7e7a9fea2c0eb269d61e3b38e450a22e754941ac78479d6c54e1faf6037881d
|
||||||
|
|
||||||
|
**target ID**::
|
||||||
|
|
||||||
|
411eba73b6f087ca51a3795d9c8c938d365e32c1
|
||||||
|
|
||||||
|
**signature**::
|
||||||
|
|
||||||
|
6834284b6b24c3204eb2fea824d82f88883a3d95e8b4a21b8c0ded553d17d17d
|
||||||
|
df9a8a7104b1258f30bed3787e6cb896fca78c58f8e03b5f18f14951a87d9a08
|
||||||
|
|
||||||
|
test 3 (immutable)
|
||||||
|
..................
|
||||||
|
|
||||||
|
value::
|
||||||
|
|
||||||
|
12:Hello World!
|
||||||
|
|
||||||
|
**target ID**::
|
||||||
|
|
||||||
|
e5f96f6f38320f0f33959cb4d3d656452117aadb
|
||||||
|
|
||||||
resources
|
resources
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ protected:
|
||||||
|
|
||||||
data_callback m_data_callback;
|
data_callback m_data_callback;
|
||||||
item m_data;
|
item m_data;
|
||||||
|
std::string m_salt;
|
||||||
};
|
};
|
||||||
|
|
||||||
class get_item_observer : public find_data_observer
|
class get_item_observer : public find_data_observer
|
||||||
|
|
|
@ -42,18 +42,30 @@ POSSIBILITY OF SUCH DAMAGE.
|
||||||
namespace libtorrent { namespace dht
|
namespace libtorrent { namespace dht
|
||||||
{
|
{
|
||||||
|
|
||||||
bool TORRENT_EXTRA_EXPORT verify_mutable_item(std::pair<char const*, int> v,
|
sha1_hash TORRENT_EXTRA_EXPORT item_target_id(
|
||||||
|
std::pair<char const*, int> v
|
||||||
|
, std::pair<char const*, int> salt
|
||||||
|
, char const* pk);
|
||||||
|
|
||||||
|
bool TORRENT_EXTRA_EXPORT verify_mutable_item(
|
||||||
|
std::pair<char const*, int> v,
|
||||||
|
std::pair<char const*, int> salt,
|
||||||
boost::uint64_t seq,
|
boost::uint64_t seq,
|
||||||
char const* pk,
|
char const* pk,
|
||||||
char const* sig);
|
char const* sig);
|
||||||
|
|
||||||
void TORRENT_EXTRA_EXPORT sign_mutable_item(std::pair<char const*, int> v,
|
void TORRENT_EXTRA_EXPORT sign_mutable_item(
|
||||||
|
std::pair<char const*, int> v,
|
||||||
|
std::pair<char const*, int> salt,
|
||||||
boost::uint64_t seq,
|
boost::uint64_t seq,
|
||||||
char const* pk,
|
char const* pk,
|
||||||
char const* sk,
|
char const* sk,
|
||||||
char* sig);
|
char* sig);
|
||||||
|
|
||||||
sha1_hash TORRENT_EXTRA_EXPORT mutable_item_cas(std::pair<char const*, int> v, boost::uint64_t seq);
|
sha1_hash TORRENT_EXTRA_EXPORT mutable_item_cas(
|
||||||
|
std::pair<char const*, int> v
|
||||||
|
, std::pair<char const*, int> salt
|
||||||
|
, boost::uint64_t seq);
|
||||||
|
|
||||||
struct TORRENT_EXTRA_EXPORT invalid_item : std::exception
|
struct TORRENT_EXTRA_EXPORT invalid_item : std::exception
|
||||||
{
|
{
|
||||||
|
@ -67,22 +79,32 @@ enum
|
||||||
item_sig_len = 64
|
item_sig_len = 64
|
||||||
};
|
};
|
||||||
|
|
||||||
struct lazy_item;
|
|
||||||
|
|
||||||
class TORRENT_EXTRA_EXPORT item
|
class TORRENT_EXTRA_EXPORT item
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
item() : m_mutable(false) {}
|
item() : m_mutable(false) {}
|
||||||
item(entry const& v) { assign(v); }
|
item(entry const& v) { assign(v); }
|
||||||
item(entry const& v, boost::uint64_t seq, char const* pk, char const* sk);
|
item(entry const& v
|
||||||
|
, std::pair<char const*, int> salt
|
||||||
|
, boost::uint64_t seq, char const* pk, char const* sk);
|
||||||
item(lazy_entry const* v) { assign(v); }
|
item(lazy_entry const* v) { assign(v); }
|
||||||
item(lazy_entry const* v, boost::uint64_t seq, char const* pk, char const* sig);
|
item(lazy_entry const* v, std::pair<char const*, int> salt
|
||||||
item(lazy_item const&);
|
, boost::uint64_t seq, char const* pk, char const* sig);
|
||||||
|
|
||||||
void assign(entry const& v) { assign(v, 0, NULL, NULL); }
|
void assign(entry const& v)
|
||||||
void assign(entry const& v, boost::uint64_t seq, char const* pk, char const* sk);
|
{
|
||||||
void assign(lazy_entry const* v) { assign(v, 0, NULL, NULL); }
|
assign(v, std::pair<char const*, int>(NULL, 0), 0, NULL, NULL);
|
||||||
bool assign(lazy_entry const* v, boost::uint64_t seq, char const* pk, char const* sig);
|
}
|
||||||
|
void assign(entry const& v
|
||||||
|
, std::pair<char const*, int> salt
|
||||||
|
, boost::uint64_t seq, char const* pk, char const* sk);
|
||||||
|
void assign(lazy_entry const* v)
|
||||||
|
{
|
||||||
|
assign(v, std::pair<char const*, int>(NULL, 0), 0, NULL, NULL);
|
||||||
|
}
|
||||||
|
bool assign(lazy_entry const* v
|
||||||
|
, std::pair<char const*, int> salt
|
||||||
|
, boost::uint64_t seq, char const* pk, char const* sig);
|
||||||
|
|
||||||
void clear() { m_value = entry(); }
|
void clear() { m_value = entry(); }
|
||||||
bool empty() const { return m_value.type() == entry::undefined_t; }
|
bool empty() const { return m_value.type() == entry::undefined_t; }
|
||||||
|
@ -98,25 +120,13 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
entry m_value;
|
entry m_value;
|
||||||
|
std::string m_salt;
|
||||||
char m_pk[item_pk_len];
|
char m_pk[item_pk_len];
|
||||||
char m_sig[item_sig_len];
|
char m_sig[item_sig_len];
|
||||||
boost::uint64_t m_seq;
|
boost::uint64_t m_seq;
|
||||||
bool m_mutable;
|
bool m_mutable;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct lazy_item
|
|
||||||
{
|
|
||||||
lazy_item(lazy_entry const* v) : value(v), pk(NULL), sig(NULL), seq(0) {}
|
|
||||||
lazy_item(lazy_entry const* v, char const* pk, char const* sig, boost::uint64_t seq);
|
|
||||||
|
|
||||||
bool is_mutable() const { return pk && sig; }
|
|
||||||
|
|
||||||
lazy_entry const* value;
|
|
||||||
char const* pk;
|
|
||||||
char const* sig;
|
|
||||||
boost::uint64_t const seq;
|
|
||||||
};
|
|
||||||
|
|
||||||
} } // namespace libtorrent::dht
|
} } // namespace libtorrent::dht
|
||||||
|
|
||||||
#endif // LIBTORRENT_ITEM_HPP
|
#endif // LIBTORRENT_ITEM_HPP
|
||||||
|
|
|
@ -140,6 +140,8 @@ struct dht_mutable_item : dht_immutable_item
|
||||||
char sig[item_sig_len];
|
char sig[item_sig_len];
|
||||||
boost::uint64_t seq;
|
boost::uint64_t seq;
|
||||||
ed25519_public_key key;
|
ed25519_public_key key;
|
||||||
|
char* salt;
|
||||||
|
int salt_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
// internal
|
// internal
|
||||||
|
|
|
@ -46,23 +46,21 @@ void get_item::got_data(lazy_entry const* v,
|
||||||
boost::uint64_t seq,
|
boost::uint64_t seq,
|
||||||
char const* sig)
|
char const* sig)
|
||||||
{
|
{
|
||||||
|
std::pair<char const*, int> salt(m_salt.c_str(), m_salt.size());
|
||||||
|
|
||||||
|
sha1_hash incoming_target = item_target_id(v->data_section(), salt, pk);
|
||||||
|
if (incoming_target != m_target) return;
|
||||||
|
|
||||||
if (pk && sig)
|
if (pk && sig)
|
||||||
{
|
{
|
||||||
if (hasher(pk, item_pk_len).final() != m_target)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (m_data.empty() || m_data.seq() < seq)
|
if (m_data.empty() || m_data.seq() < seq)
|
||||||
{
|
{
|
||||||
if (!m_data.assign(v, seq, pk, sig))
|
if (!m_data.assign(v, salt, seq, pk, sig))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (m_data.empty())
|
else if (m_data.empty())
|
||||||
{
|
{
|
||||||
std::pair<char const*, int> buf = v->data_section();
|
|
||||||
if (hasher(buf.first, buf.second).final() != m_target)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_data.assign(v);
|
m_data.assign(v);
|
||||||
bool put_requested = m_data_callback(m_data);
|
bool put_requested = m_data_callback(m_data);
|
||||||
if (put_requested)
|
if (put_requested)
|
||||||
|
|
|
@ -45,8 +45,9 @@ namespace libtorrent { namespace dht
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
enum { canonical_length = 1100 };
|
enum { canonical_length = 1200 };
|
||||||
int canonical_string(std::pair<char const*, int> v, boost::uint64_t seq, char out[canonical_length])
|
int canonical_string(std::pair<char const*, int> v, boost::uint64_t seq
|
||||||
|
, std::pair<char const*, int> salt, char out[canonical_length])
|
||||||
{
|
{
|
||||||
// v must be valid bencoding!
|
// v must be valid bencoding!
|
||||||
#ifdef TORRENT_DEBUG
|
#ifdef TORRENT_DEBUG
|
||||||
|
@ -54,15 +55,48 @@ namespace
|
||||||
error_code ec;
|
error_code ec;
|
||||||
TORRENT_ASSERT(lazy_bdecode(v.first, v.first + v.second, e, ec) == 0);
|
TORRENT_ASSERT(lazy_bdecode(v.first, v.first + v.second, e, ec) == 0);
|
||||||
#endif
|
#endif
|
||||||
int len = snprintf(out, canonical_length, "3:seqi%" PRId64 "e1:v", seq);
|
char* ptr = out;
|
||||||
memcpy(out + len, v.first, v.second);
|
|
||||||
len += v.second;
|
int left = canonical_length - (ptr - out);
|
||||||
TORRENT_ASSERT(len <= canonical_length);
|
if (salt.second > 0)
|
||||||
return len;
|
{
|
||||||
|
ptr += snprintf(ptr, left, "4:salt%d:", salt.second);
|
||||||
|
left = canonical_length - (ptr - out);
|
||||||
|
memcpy(ptr, salt.first, (std::min)(salt.second, left));
|
||||||
|
ptr += (std::min)(salt.second, left);
|
||||||
|
left = canonical_length - (ptr - out);
|
||||||
|
}
|
||||||
|
ptr += snprintf(ptr, canonical_length - (ptr - out)
|
||||||
|
, "3:seqi%" PRId64 "e1:v", seq);
|
||||||
|
left = canonical_length - (ptr - out);
|
||||||
|
memcpy(ptr, v.first, (std::min)(v.second, left));
|
||||||
|
ptr += (std::min)(v.second, left);
|
||||||
|
TORRENT_ASSERT((ptr - out) <= canonical_length);
|
||||||
|
return ptr - out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool verify_mutable_item(std::pair<char const*, int> v,
|
sha1_hash item_target_id(
|
||||||
|
std::pair<char const*, int> v
|
||||||
|
, std::pair<char const*, int> salt
|
||||||
|
, char const* pk)
|
||||||
|
{
|
||||||
|
hasher h;
|
||||||
|
if (pk)
|
||||||
|
{
|
||||||
|
h.update(pk, item_pk_len);
|
||||||
|
if (salt.second > 0) h.update(salt.first, salt.second);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
h.update(v.first, v.second);
|
||||||
|
}
|
||||||
|
return h.final();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool verify_mutable_item(
|
||||||
|
std::pair<char const*, int> v,
|
||||||
|
std::pair<char const*, int> salt,
|
||||||
boost::uint64_t seq,
|
boost::uint64_t seq,
|
||||||
char const* pk,
|
char const* pk,
|
||||||
char const* sig)
|
char const* sig)
|
||||||
|
@ -74,7 +108,7 @@ bool verify_mutable_item(std::pair<char const*, int> v,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char str[canonical_length];
|
char str[canonical_length];
|
||||||
int len = canonical_string(v, seq, str);
|
int len = canonical_string(v, seq, salt, str);
|
||||||
|
|
||||||
return ed25519_verify((unsigned char const*)sig,
|
return ed25519_verify((unsigned char const*)sig,
|
||||||
(unsigned char const*)str,
|
(unsigned char const*)str,
|
||||||
|
@ -82,7 +116,9 @@ bool verify_mutable_item(std::pair<char const*, int> v,
|
||||||
(unsigned char const*)pk) == 1;
|
(unsigned char const*)pk) == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sign_mutable_item(std::pair<char const*, int> v,
|
void sign_mutable_item(
|
||||||
|
std::pair<char const*, int> v,
|
||||||
|
std::pair<char const*, int> salt,
|
||||||
boost::uint64_t seq,
|
boost::uint64_t seq,
|
||||||
char const* pk,
|
char const* pk,
|
||||||
char const* sk,
|
char const* sk,
|
||||||
|
@ -95,7 +131,7 @@ void sign_mutable_item(std::pair<char const*, int> v,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char str[canonical_length];
|
char str[canonical_length];
|
||||||
int len = canonical_string(v, seq, str);
|
int len = canonical_string(v, seq, salt, str);
|
||||||
|
|
||||||
ed25519_sign((unsigned char*)sig,
|
ed25519_sign((unsigned char*)sig,
|
||||||
(unsigned char const*)str,
|
(unsigned char const*)str,
|
||||||
|
@ -105,35 +141,31 @@ void sign_mutable_item(std::pair<char const*, int> v,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
sha1_hash mutable_item_cas(std::pair<char const*, int> v, boost::uint64_t seq)
|
sha1_hash mutable_item_cas(std::pair<char const*, int> v
|
||||||
|
, std::pair<char const*, int> salt
|
||||||
|
, boost::uint64_t seq)
|
||||||
{
|
{
|
||||||
char str[canonical_length];
|
char str[canonical_length];
|
||||||
int len = canonical_string(v, seq, str);
|
int len = canonical_string(v, seq, salt, str);
|
||||||
return hasher(str, len).final();
|
return hasher(str, len).final();
|
||||||
}
|
}
|
||||||
|
|
||||||
item::item(entry const& v, boost::uint64_t seq, char const* pk, char const* sk)
|
item::item(entry const& v
|
||||||
|
, std::pair<char const*, int> salt
|
||||||
|
, boost::uint64_t seq, char const* pk, char const* sk)
|
||||||
{
|
{
|
||||||
assign(v, seq, pk, sk);
|
assign(v, salt, seq, pk, sk);
|
||||||
}
|
}
|
||||||
|
|
||||||
item::item(lazy_entry const* v, boost::uint64_t seq, char const* pk, char const* sig)
|
item::item(lazy_entry const* v, std::pair<char const*, int> salt
|
||||||
|
, boost::uint64_t seq, char const* pk, char const* sig)
|
||||||
{
|
{
|
||||||
if (!assign(v, seq, pk, sig))
|
if (!assign(v, salt, seq, pk, sig))
|
||||||
throw invalid_item();
|
throw invalid_item();
|
||||||
}
|
}
|
||||||
|
|
||||||
item::item(lazy_item const& i)
|
void item::assign(entry const& v, std::pair<char const*, int> salt
|
||||||
: m_seq(i.seq)
|
, boost::uint64_t seq, char const* pk, char const* sk)
|
||||||
, m_mutable(i.is_mutable())
|
|
||||||
{
|
|
||||||
m_value = *i.value;
|
|
||||||
// if this is a mutable item lazy_item will have already verified it
|
|
||||||
memcpy(m_pk, i.pk, item_pk_len);
|
|
||||||
memcpy(m_sig, i.sig, item_sig_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void item::assign(entry const& v, boost::uint64_t seq, char const* pk, char const* sk)
|
|
||||||
{
|
{
|
||||||
m_value = v;
|
m_value = v;
|
||||||
if (pk && sk)
|
if (pk && sk)
|
||||||
|
@ -141,7 +173,8 @@ void item::assign(entry const& v, boost::uint64_t seq, char const* pk, char cons
|
||||||
char buffer[1000];
|
char buffer[1000];
|
||||||
int bsize = bencode(buffer, v);
|
int bsize = bencode(buffer, v);
|
||||||
TORRENT_ASSERT(bsize <= 1000);
|
TORRENT_ASSERT(bsize <= 1000);
|
||||||
sign_mutable_item(std::make_pair(buffer, bsize), seq, pk, sk, m_sig);
|
sign_mutable_item(std::make_pair(buffer, bsize)
|
||||||
|
, salt, seq, pk, sk, m_sig);
|
||||||
memcpy(m_pk, pk, item_pk_len);
|
memcpy(m_pk, pk, item_pk_len);
|
||||||
m_seq = seq;
|
m_seq = seq;
|
||||||
m_mutable = true;
|
m_mutable = true;
|
||||||
|
@ -150,15 +183,19 @@ void item::assign(entry const& v, boost::uint64_t seq, char const* pk, char cons
|
||||||
m_mutable = false;
|
m_mutable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool item::assign(lazy_entry const* v, boost::uint64_t seq, char const* pk, char const* sig)
|
bool item::assign(lazy_entry const* v
|
||||||
|
, std::pair<char const*, int> salt
|
||||||
|
, boost::uint64_t seq, char const* pk, char const* sig)
|
||||||
{
|
{
|
||||||
TORRENT_ASSERT(v->data_section().second <= 1000);
|
TORRENT_ASSERT(v->data_section().second <= 1000);
|
||||||
if (pk && sig)
|
if (pk && sig)
|
||||||
{
|
{
|
||||||
if (!verify_mutable_item(v->data_section(), seq, pk, sig))
|
if (!verify_mutable_item(v->data_section(), salt, seq, pk, sig))
|
||||||
return false;
|
return false;
|
||||||
memcpy(m_pk, pk, item_pk_len);
|
memcpy(m_pk, pk, item_pk_len);
|
||||||
memcpy(m_sig, sig, item_sig_len);
|
memcpy(m_sig, sig, item_sig_len);
|
||||||
|
if (salt.second > 0)
|
||||||
|
m_salt.assign(salt.first, salt.second);
|
||||||
m_seq = seq;
|
m_seq = seq;
|
||||||
m_mutable = true;
|
m_mutable = true;
|
||||||
}
|
}
|
||||||
|
@ -174,14 +211,8 @@ sha1_hash item::cas()
|
||||||
TORRENT_ASSERT(m_mutable);
|
TORRENT_ASSERT(m_mutable);
|
||||||
char buffer[1000];
|
char buffer[1000];
|
||||||
int bsize = bencode(buffer, m_value);
|
int bsize = bencode(buffer, m_value);
|
||||||
return mutable_item_cas(std::make_pair(buffer, bsize), m_seq);
|
return mutable_item_cas(std::make_pair(buffer, bsize)
|
||||||
}
|
, std::pair<char const*, int>(m_salt.c_str(), m_salt.size()), m_seq);
|
||||||
|
|
||||||
lazy_item::lazy_item(lazy_entry const* v, char const* pk, char const* sig, boost::uint64_t seq)
|
|
||||||
: value(v), pk(pk), sig(sig), seq(seq)
|
|
||||||
{
|
|
||||||
if (is_mutable() && !verify_mutable_item(v->data_section(), seq, pk, sig))
|
|
||||||
throw invalid_item();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} } // namespace libtorrent::dht
|
} } // namespace libtorrent::dht
|
||||||
|
|
|
@ -904,11 +904,12 @@ void node_impl::incoming_request(msg const& m, entry& e)
|
||||||
{"k", lazy_entry::string_t, item_pk_len, key_desc_t::optional},
|
{"k", lazy_entry::string_t, item_pk_len, key_desc_t::optional},
|
||||||
{"sig", lazy_entry::string_t, item_sig_len, key_desc_t::optional},
|
{"sig", lazy_entry::string_t, item_sig_len, key_desc_t::optional},
|
||||||
{"cas", lazy_entry::string_t, 20, key_desc_t::optional},
|
{"cas", lazy_entry::string_t, 20, key_desc_t::optional},
|
||||||
|
{"salt", lazy_entry::string_t, 0, key_desc_t::optional},
|
||||||
};
|
};
|
||||||
|
|
||||||
// attempt to parse the message
|
// attempt to parse the message
|
||||||
lazy_entry const* msg_keys[6];
|
lazy_entry const* msg_keys[7];
|
||||||
if (!verify_message(arg_ent, msg_desc, msg_keys, 6, error_string, sizeof(error_string)))
|
if (!verify_message(arg_ent, msg_desc, msg_keys, 7, error_string, sizeof(error_string)))
|
||||||
{
|
{
|
||||||
incoming_error(e, error_string);
|
incoming_error(e, error_string);
|
||||||
return;
|
return;
|
||||||
|
@ -917,6 +918,14 @@ void node_impl::incoming_request(msg const& m, entry& e)
|
||||||
// is this a mutable put?
|
// is this a mutable put?
|
||||||
bool mutable_put = (msg_keys[2] && msg_keys[3] && msg_keys[4]);
|
bool mutable_put = (msg_keys[2] && msg_keys[3] && msg_keys[4]);
|
||||||
|
|
||||||
|
// public key (only set if it's a mutable put)
|
||||||
|
char const* pk = NULL;
|
||||||
|
if (msg_keys[3]) pk = msg_keys[3]->string_ptr();
|
||||||
|
|
||||||
|
// signature (only set if it's a mutable put)
|
||||||
|
char const* sig = NULL;
|
||||||
|
if (msg_keys[4]) sig = msg_keys[4]->string_ptr();
|
||||||
|
|
||||||
// pointer and length to the whole entry
|
// pointer and length to the whole entry
|
||||||
std::pair<char const*, int> buf = msg_keys[1]->data_section();
|
std::pair<char const*, int> buf = msg_keys[1]->data_section();
|
||||||
if (buf.second > 1000 || buf.second <= 0)
|
if (buf.second > 1000 || buf.second <= 0)
|
||||||
|
@ -925,15 +934,23 @@ void node_impl::incoming_request(msg const& m, entry& e)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sha1_hash target;
|
std::pair<char const*, int> salt(NULL, 0);
|
||||||
if (!mutable_put)
|
if (msg_keys[6])
|
||||||
target = hasher(buf.first, buf.second).final();
|
salt = std::pair<char const*, int>(
|
||||||
else
|
msg_keys[6]->string_ptr(), msg_keys[6]->string_length());
|
||||||
target = hasher(msg_keys[3]->string_ptr(), item_pk_len).final();
|
if (salt.second > 64)
|
||||||
|
{
|
||||||
|
incoming_error(e, "salt too big", 207);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// fprintf(stderr, "%s PUT target: %s\n"
|
sha1_hash target = item_target_id(buf, salt, pk);
|
||||||
|
|
||||||
|
// fprintf(stderr, "%s PUT target: %s salt: %s key: %s\n"
|
||||||
// , mutable_put ? "mutable":"immutable"
|
// , mutable_put ? "mutable":"immutable"
|
||||||
// , to_hex(target.to_string()).c_str());
|
// , to_hex(target.to_string()).c_str()
|
||||||
|
// , salt.second > 0 ? std::string(salt.first, salt.second).c_str() : ""
|
||||||
|
// , pk ? to_hex(std::string(pk, 32)).c_str() : "");
|
||||||
|
|
||||||
// verify the write-token. tokens are only valid to write to
|
// verify the write-token. tokens are only valid to write to
|
||||||
// specific target hashes. it must match the one we got a "get" for
|
// specific target hashes. it must match the one we got a "get" for
|
||||||
|
@ -983,19 +1000,16 @@ void node_impl::incoming_request(msg const& m, entry& e)
|
||||||
|
|
||||||
#ifdef TORRENT_USE_VALGRIND
|
#ifdef TORRENT_USE_VALGRIND
|
||||||
VALGRIND_CHECK_MEM_IS_DEFINED(msg_keys[4]->string_ptr(), item_sig_len);
|
VALGRIND_CHECK_MEM_IS_DEFINED(msg_keys[4]->string_ptr(), item_sig_len);
|
||||||
VALGRIND_CHECK_MEM_IS_DEFINED(msg_keys[3]->string_ptr(), item_pk_len);
|
VALGRIND_CHECK_MEM_IS_DEFINED(pk, item_pk_len);
|
||||||
#endif
|
#endif
|
||||||
// msg_keys[4] is the signature, msg_keys[3] is the public key
|
// msg_keys[4] is the signature, msg_keys[3] is the public key
|
||||||
if (!verify_mutable_item(msg_keys[1]->data_section()
|
if (!verify_mutable_item(buf, salt
|
||||||
, msg_keys[2]->int_value()
|
, msg_keys[2]->int_value(), pk, sig))
|
||||||
, msg_keys[3]->string_ptr()
|
|
||||||
, msg_keys[4]->string_ptr()))
|
|
||||||
{
|
{
|
||||||
incoming_error(e, "invalid signature", 206);
|
incoming_error(e, "invalid signature", 206);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sha1_hash target = hasher(msg_keys[3]->string_ptr(), msg_keys[3]->string_length()).final();
|
|
||||||
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())
|
||||||
{
|
{
|
||||||
|
@ -1011,16 +1025,25 @@ void node_impl::incoming_request(msg const& m, entry& e)
|
||||||
, boost::bind(&dht_mutable_table_t::value_type::second, _1)));
|
, boost::bind(&dht_mutable_table_t::value_type::second, _1)));
|
||||||
TORRENT_ASSERT(j != m_mutable_table.end());
|
TORRENT_ASSERT(j != m_mutable_table.end());
|
||||||
free(j->second.value);
|
free(j->second.value);
|
||||||
|
free(j->second.salt);
|
||||||
m_mutable_table.erase(j);
|
m_mutable_table.erase(j);
|
||||||
}
|
}
|
||||||
dht_mutable_item to_add;
|
dht_mutable_item to_add;
|
||||||
to_add.value = (char*)malloc(buf.second);
|
to_add.value = (char*)malloc(buf.second);
|
||||||
to_add.size = buf.second;
|
to_add.size = buf.second;
|
||||||
to_add.seq = msg_keys[2]->int_value();
|
to_add.seq = msg_keys[2]->int_value();
|
||||||
memcpy(to_add.sig, msg_keys[4]->string_ptr(), sizeof(to_add.sig));
|
to_add.salt = NULL;
|
||||||
|
to_add.salt_size = 0;
|
||||||
|
if (salt.second > 0)
|
||||||
|
{
|
||||||
|
to_add.salt = (char*)malloc(salt.second);
|
||||||
|
to_add.salt_size = salt.second;
|
||||||
|
memcpy(to_add.salt, salt.first, salt.second);
|
||||||
|
}
|
||||||
|
memcpy(to_add.sig, sig, 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));
|
memcpy(&to_add.key, pk, 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));
|
||||||
|
@ -1037,7 +1060,10 @@ void node_impl::incoming_request(msg const& m, entry& e)
|
||||||
// matches the expected value before replacing it
|
// matches the expected value before replacing it
|
||||||
if (msg_keys[5])
|
if (msg_keys[5])
|
||||||
{
|
{
|
||||||
sha1_hash h = mutable_item_cas(std::make_pair(item->value, item->size), item->seq);
|
sha1_hash h = mutable_item_cas(
|
||||||
|
std::make_pair(item->value, item->size)
|
||||||
|
, std::make_pair(item->salt, item->salt_size)
|
||||||
|
, item->seq);
|
||||||
|
|
||||||
if (h != sha1_hash(msg_keys[5]->string_ptr()))
|
if (h != sha1_hash(msg_keys[5]->string_ptr()))
|
||||||
{
|
{
|
||||||
|
|
|
@ -147,7 +147,8 @@ void send_dht_request(node_impl& node, char const* msg, udp::endpoint const& ep
|
||||||
, char const* target = 0, entry const* value = 0
|
, char const* target = 0, entry const* value = 0
|
||||||
, bool scrape = false, bool seed = false
|
, bool scrape = false, bool seed = false
|
||||||
, std::string const key = std::string(), std::string const sig = std::string()
|
, std::string const key = std::string(), std::string const sig = std::string()
|
||||||
, int seq = -1, char const* cas = 0, sha1_hash const* nid = NULL)
|
, int seq = -1, char const* cas = 0, sha1_hash const* nid = NULL
|
||||||
|
, char const* put_salt = NULL)
|
||||||
{
|
{
|
||||||
// we're about to clear out the backing buffer
|
// we're about to clear out the backing buffer
|
||||||
// for this lazy_entry, so we better clear it now
|
// for this lazy_entry, so we better clear it now
|
||||||
|
@ -171,6 +172,7 @@ void send_dht_request(node_impl& node, char const* msg, udp::endpoint const& ep
|
||||||
if (seed) a["seed"] = 1;
|
if (seed) a["seed"] = 1;
|
||||||
if (seq >= 0) a["seq"] = seq;
|
if (seq >= 0) a["seq"] = seq;
|
||||||
if (cas) a["cas"] = std::string(cas, 20);
|
if (cas) a["cas"] = std::string(cas, 20);
|
||||||
|
if (put_salt) a["salt"] = put_salt;
|
||||||
char msg_buf[1500];
|
char msg_buf[1500];
|
||||||
int size = bencode(msg_buf, e);
|
int size = bencode(msg_buf, e);
|
||||||
#if defined TORRENT_DEBUG && TORRENT_USE_IOSTREAM
|
#if defined TORRENT_DEBUG && TORRENT_USE_IOSTREAM
|
||||||
|
@ -747,214 +749,247 @@ int test_main()
|
||||||
|
|
||||||
announce_immutable_items(node, eps, items, sizeof(items)/sizeof(items[0]));
|
announce_immutable_items(node, eps, items, sizeof(items)/sizeof(items[0]));
|
||||||
|
|
||||||
// ==== get / put mutable items ===
|
|
||||||
|
|
||||||
fprintf(stderr, "generating ed25519 keys\n");
|
|
||||||
unsigned char seed[32];
|
|
||||||
ed25519_create_seed(seed);
|
|
||||||
char private_key[item_sk_len];
|
|
||||||
char public_key[item_pk_len];
|
|
||||||
|
|
||||||
ed25519_create_keypair((unsigned char*)public_key, (unsigned char*)private_key, seed);
|
|
||||||
fprintf(stderr, "pub: %s priv: %s\n"
|
|
||||||
, to_hex(std::string(public_key, item_pk_len)).c_str()
|
|
||||||
, to_hex(std::string(private_key, item_sk_len)).c_str());
|
|
||||||
|
|
||||||
TEST_CHECK(ret);
|
|
||||||
|
|
||||||
send_dht_request(node, "get", source, &response, "10", 0
|
|
||||||
, 0, no, 0, (char*)&hasher(public_key, item_pk_len).final()[0]
|
|
||||||
, 0, false, false, std::string(), std::string(), 64);
|
|
||||||
|
|
||||||
key_desc_t desc[] =
|
|
||||||
{
|
|
||||||
{ "r", lazy_entry::dict_t, 0, key_desc_t::parse_children },
|
|
||||||
{ "id", lazy_entry::string_t, 20, 0},
|
|
||||||
{ "token", 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, desc, parsed, 5, error_string, sizeof(error_string));
|
|
||||||
if (ret)
|
|
||||||
{
|
|
||||||
TEST_EQUAL(parsed[4]->string_value(), "r");
|
|
||||||
token = parsed[2]->string_value();
|
|
||||||
fprintf(stderr, "got token: %s\n", token.c_str());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
char signature[item_sig_len];
|
|
||||||
char buffer[1200];
|
|
||||||
int seq = 4;
|
|
||||||
std::pair<const char*, int> itemv(buffer, bencode(buffer, items[0].ent));
|
|
||||||
sign_mutable_item(itemv, seq, public_key, private_key, signature);
|
|
||||||
TEST_EQUAL(verify_mutable_item(itemv, seq, public_key, signature), true);
|
|
||||||
#ifdef TORRENT_USE_VALGRIND
|
|
||||||
VALGRIND_CHECK_MEM_IS_DEFINED(signature, item_sig_len);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
send_dht_request(node, "put", source, &response, "10", 0
|
|
||||||
, 0, token, 0, 0, &items[0].ent, false, false
|
|
||||||
, std::string(public_key, item_pk_len)
|
|
||||||
, std::string(signature, item_sig_len), seq);
|
|
||||||
|
|
||||||
key_desc_t desc2[] =
|
key_desc_t desc2[] =
|
||||||
{
|
{
|
||||||
{ "y", lazy_entry::string_t, 1, 0 }
|
{ "y", lazy_entry::string_t, 1, 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
send_dht_request(node, "get", source, &response, "10", 0
|
|
||||||
, 0, no, 0, (char*)&hasher(public_key, item_pk_len).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());
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// also test that invalid signatures fail!
|
|
||||||
|
|
||||||
itemv.second = bencode(buffer, items[0].ent);
|
|
||||||
sign_mutable_item(itemv, seq, public_key, private_key, signature);
|
|
||||||
TEST_EQUAL(verify_mutable_item(itemv, seq, public_key, signature), 1);
|
|
||||||
#ifdef TORRENT_USE_VALGRIND
|
|
||||||
VALGRIND_CHECK_MEM_IS_DEFINED(signature, item_sig_len);
|
|
||||||
#endif
|
|
||||||
// break the signature
|
|
||||||
signature[2] ^= 0xaa;
|
|
||||||
|
|
||||||
TEST_CHECK(verify_mutable_item(itemv, seq, public_key, signature) != 1);
|
|
||||||
|
|
||||||
send_dht_request(node, "put", source, &response, "10", 0
|
|
||||||
, 0, token, 0, 0, &items[0].ent, false, false
|
|
||||||
, std::string(public_key, item_pk_len)
|
|
||||||
, std::string(signature, item_sig_len), seq);
|
|
||||||
|
|
||||||
key_desc_t desc_error[] =
|
key_desc_t desc_error[] =
|
||||||
{
|
{
|
||||||
{ "e", lazy_entry::list_t, 2, 0 },
|
{ "e", lazy_entry::list_t, 2, 0 },
|
||||||
{ "y", lazy_entry::string_t, 1, 0},
|
{ "y", lazy_entry::string_t, 1, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
ret = verify_message(&response, desc_error, parsed, 2, error_string, sizeof(error_string));
|
// ==== get / put mutable items ===
|
||||||
if (ret)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "put response: %s\n", print_entry(response).c_str());
|
|
||||||
TEST_EQUAL(parsed[1]->string_value(), "e");
|
|
||||||
// 206 is the code for invalid signature
|
|
||||||
TEST_EQUAL(parsed[0]->list_int_value_at(0), 206);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, " invalid put response: %s\n%s\n"
|
|
||||||
, error_string, print_entry(response).c_str());
|
|
||||||
TEST_ERROR(error_string);
|
|
||||||
}
|
|
||||||
|
|
||||||
// === test CAS put ===
|
std::pair<const char*, int> itemv;
|
||||||
|
std::pair<char const*, int> empty_salt(NULL, 0);
|
||||||
|
char signature[item_sig_len];
|
||||||
|
char buffer[1200];
|
||||||
|
int seq = 4;
|
||||||
|
char private_key[item_sk_len];
|
||||||
|
char public_key[item_pk_len];
|
||||||
|
for (int with_salt = 0; with_salt < 2; ++with_salt)
|
||||||
|
{
|
||||||
|
seq = 4;
|
||||||
|
fprintf(stderr, "\nTEST GET/PUT%s \ngenerating ed25519 keys\n\n"
|
||||||
|
, with_salt ? " with-salt" : " no-salt");
|
||||||
|
unsigned char seed[32];
|
||||||
|
ed25519_create_seed(seed);
|
||||||
|
|
||||||
// this is the hash that we expect to be there
|
ed25519_create_keypair((unsigned char*)public_key, (unsigned char*)private_key, seed);
|
||||||
sha1_hash cas = mutable_item_cas(itemv, seq);
|
fprintf(stderr, "pub: %s priv: %s\n"
|
||||||
// increment sequence number
|
, to_hex(std::string(public_key, item_pk_len)).c_str()
|
||||||
++seq;
|
, to_hex(std::string(private_key, item_sk_len)).c_str());
|
||||||
// put item 1
|
|
||||||
itemv.second = bencode(buffer, items[1].ent);
|
TEST_CHECK(ret);
|
||||||
sign_mutable_item(itemv, seq, public_key, private_key, signature);
|
|
||||||
TEST_EQUAL(verify_mutable_item(itemv, seq, public_key, signature), 1);
|
std::pair<const char*, int> salt(NULL, 0);
|
||||||
|
if (with_salt)
|
||||||
|
salt = std::pair<char const*, int>("foobar", 6);
|
||||||
|
|
||||||
|
hasher h(public_key, 32);
|
||||||
|
if (with_salt) h.update(salt.first, salt.second);
|
||||||
|
sha1_hash target_id = h.final();
|
||||||
|
|
||||||
|
fprintf(stderr, "target_id: %s\n"
|
||||||
|
, to_hex(target_id.to_string()).c_str());
|
||||||
|
|
||||||
|
send_dht_request(node, "get", source, &response, "10", 0
|
||||||
|
, 0, no, 0, (char*)&target_id[0]
|
||||||
|
, 0, false, false, std::string(), std::string(), 64);
|
||||||
|
|
||||||
|
key_desc_t desc[] =
|
||||||
|
{
|
||||||
|
{ "r", lazy_entry::dict_t, 0, key_desc_t::parse_children },
|
||||||
|
{ "id", lazy_entry::string_t, 20, 0},
|
||||||
|
{ "token", 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, desc, parsed, 5, error_string, sizeof(error_string));
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
TEST_EQUAL(parsed[4]->string_value(), "r");
|
||||||
|
token = parsed[2]->string_value();
|
||||||
|
fprintf(stderr, "get response: %s\n"
|
||||||
|
, print_entry(response).c_str());
|
||||||
|
fprintf(stderr, "got token: %s\n", to_hex(token).c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
itemv = std::pair<char const*, int>(buffer, bencode(buffer, items[0].ent));
|
||||||
|
sign_mutable_item(itemv, salt, seq, public_key, private_key, signature);
|
||||||
|
TEST_EQUAL(verify_mutable_item(itemv, salt, seq, public_key, signature), true);
|
||||||
#ifdef TORRENT_USE_VALGRIND
|
#ifdef TORRENT_USE_VALGRIND
|
||||||
VALGRIND_CHECK_MEM_IS_DEFINED(signature, item_sig_len);
|
VALGRIND_CHECK_MEM_IS_DEFINED(signature, item_sig_len);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
send_dht_request(node, "put", source, &response, "10", 0
|
send_dht_request(node, "put", source, &response, "10", 0
|
||||||
, 0, token, 0, 0, &items[1].ent, false, false
|
, 0, token, 0, 0, &items[0].ent, false, false
|
||||||
, std::string(public_key, item_pk_len)
|
, std::string(public_key, item_pk_len)
|
||||||
, std::string(signature, item_sig_len), seq
|
, std::string(signature, item_sig_len), seq, NULL, NULL, salt.first);
|
||||||
, (char const*)&cas[0]);
|
|
||||||
|
|
||||||
ret = verify_message(&response, desc2, parsed, 1, error_string, sizeof(error_string));
|
ret = verify_message(&response, desc2, parsed, 1, error_string, sizeof(error_string));
|
||||||
if (ret)
|
if (ret)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "put response: %s\n"
|
fprintf(stderr, "put response: %s\n"
|
||||||
, print_entry(response).c_str());
|
, print_entry(response).c_str());
|
||||||
TEST_EQUAL(parsed[0]->string_value(), "r");
|
TEST_EQUAL(parsed[0]->string_value(), "r");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf(stderr, " invalid put response: %s\n%s\n"
|
fprintf(stderr, " invalid put response: %s\n%s\n"
|
||||||
, error_string, print_entry(response).c_str());
|
, error_string, print_entry(response).c_str());
|
||||||
TEST_ERROR(error_string);
|
TEST_ERROR(error_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
// put the same message again. This should fail because the
|
send_dht_request(node, "get", source, &response, "10", 0
|
||||||
// CAS hash is outdated, it's not the hash of the value that's
|
, 0, no, 0, (char*)&target_id[0]
|
||||||
// stored anymore
|
, 0, false, false, std::string(), std::string(), 64);
|
||||||
send_dht_request(node, "put", source, &response, "10", 0
|
|
||||||
, 0, token, 0, 0, &items[1].ent, false, false
|
fprintf(stderr, "target_id: %s\n"
|
||||||
, std::string(public_key, item_pk_len)
|
, to_hex(target_id.to_string()).c_str());
|
||||||
, std::string(signature, item_sig_len), seq
|
|
||||||
, (char const*)&cas[0]);
|
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
|
||||||
|
{
|
||||||
|
fprintf(stderr, "get response: %s\n"
|
||||||
|
, print_entry(response).c_str());
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
// also test that invalid signatures fail!
|
||||||
|
|
||||||
|
itemv.second = bencode(buffer, items[0].ent);
|
||||||
|
sign_mutable_item(itemv, salt, seq, public_key, private_key, signature);
|
||||||
|
TEST_EQUAL(verify_mutable_item(itemv, salt, seq, public_key, signature), 1);
|
||||||
|
#ifdef TORRENT_USE_VALGRIND
|
||||||
|
VALGRIND_CHECK_MEM_IS_DEFINED(signature, item_sig_len);
|
||||||
|
#endif
|
||||||
|
// break the signature
|
||||||
|
signature[2] ^= 0xaa;
|
||||||
|
|
||||||
|
fprintf(stderr, "PUT broken signature\n");
|
||||||
|
|
||||||
|
TEST_CHECK(verify_mutable_item(itemv, salt, seq, public_key, signature) != 1);
|
||||||
|
|
||||||
|
send_dht_request(node, "put", source, &response, "10", 0
|
||||||
|
, 0, token, 0, 0, &items[0].ent, false, false
|
||||||
|
, std::string(public_key, item_pk_len)
|
||||||
|
, std::string(signature, item_sig_len), seq, NULL, NULL, salt.first);
|
||||||
|
|
||||||
|
ret = verify_message(&response, desc_error, 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");
|
||||||
|
// 206 is the code for invalid signature
|
||||||
|
TEST_EQUAL(parsed[0]->list_int_value_at(0), 206);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, " invalid put response: %s\n%s\n"
|
||||||
|
, error_string, print_entry(response).c_str());
|
||||||
|
TEST_ERROR(error_string);
|
||||||
|
}
|
||||||
|
|
||||||
|
// === test CAS put ===
|
||||||
|
|
||||||
|
// this is the hash that we expect to be there
|
||||||
|
sha1_hash cas = mutable_item_cas(itemv, salt, seq);
|
||||||
|
// increment sequence number
|
||||||
|
++seq;
|
||||||
|
// put item 1
|
||||||
|
itemv.second = bencode(buffer, items[1].ent);
|
||||||
|
sign_mutable_item(itemv, salt, seq, public_key, private_key, signature);
|
||||||
|
TEST_EQUAL(verify_mutable_item(itemv, salt, seq, public_key, signature), 1);
|
||||||
|
#ifdef TORRENT_USE_VALGRIND
|
||||||
|
VALGRIND_CHECK_MEM_IS_DEFINED(signature, item_sig_len);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TEST_CHECK(item_target_id(itemv, salt, public_key) == target_id);
|
||||||
|
|
||||||
|
fprintf(stderr, "PUT CAS 1\n");
|
||||||
|
|
||||||
|
send_dht_request(node, "put", source, &response, "10", 0
|
||||||
|
, 0, token, 0, 0, &items[1].ent, false, false
|
||||||
|
, std::string(public_key, item_pk_len)
|
||||||
|
, std::string(signature, item_sig_len), seq
|
||||||
|
, (char const*)&cas[0], NULL, salt.first);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "PUT CAS 2\n");
|
||||||
|
|
||||||
|
// 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_request(node, "put", source, &response, "10", 0
|
||||||
|
, 0, token, 0, 0, &items[1].ent, false, false
|
||||||
|
, std::string(public_key, item_pk_len)
|
||||||
|
, std::string(signature, item_sig_len), seq
|
||||||
|
, (char const*)&cas[0], NULL, salt.first);
|
||||||
|
|
||||||
|
ret = verify_message(&response, desc_error, 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);
|
||||||
|
}
|
||||||
|
|
||||||
ret = verify_message(&response, desc_error, 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
|
// test routing table
|
||||||
|
@ -1589,7 +1624,7 @@ int test_main()
|
||||||
g_sent_packets.clear();
|
g_sent_packets.clear();
|
||||||
|
|
||||||
itemv.second = bencode(buffer, items[0].ent);
|
itemv.second = bencode(buffer, items[0].ent);
|
||||||
sign_mutable_item(itemv, seq, public_key, private_key, signature);
|
sign_mutable_item(itemv, empty_salt, seq, public_key, private_key, signature);
|
||||||
send_dht_response(node, response, initial_node, nodes_t(), "10", 1234, std::set<tcp::endpoint>()
|
send_dht_response(node, response, initial_node, nodes_t(), "10", 1234, std::set<tcp::endpoint>()
|
||||||
, NULL, &items[0].ent, std::string(public_key, item_pk_len), std::string(signature, item_sig_len), seq);
|
, NULL, &items[0].ent, std::string(public_key, item_pk_len), std::string(signature, item_sig_len), seq);
|
||||||
|
|
||||||
|
@ -1723,7 +1758,7 @@ int test_main()
|
||||||
node.m_table.add_node(nodes[i]);
|
node.m_table.add_node(nodes[i]);
|
||||||
|
|
||||||
sha1_hash target = hasher(public_key, item_pk_len).final();
|
sha1_hash target = hasher(public_key, item_pk_len).final();
|
||||||
g_put_item.assign(items[0].ent, seq, public_key, private_key);
|
g_put_item.assign(items[0].ent, empty_salt, seq, public_key, private_key);
|
||||||
std::string sig(g_put_item.sig(), item_sig_len);
|
std::string sig(g_put_item.sig(), item_sig_len);
|
||||||
node.get_item(target, get_item_cb);
|
node.get_item(target, get_item_cb);
|
||||||
|
|
||||||
|
@ -1794,18 +1829,44 @@ int test_main()
|
||||||
|
|
||||||
} while (false);
|
} while (false);
|
||||||
|
|
||||||
// test vector
|
// test vector 1
|
||||||
|
|
||||||
|
// test content
|
||||||
|
std::pair<char const*, int> test_content("12:Hello World!", 15);
|
||||||
|
// test salt
|
||||||
|
std::pair<char const*, int> test_salt("foobar", 6);
|
||||||
|
|
||||||
from_hex("77ff84905a91936367c01360803104f92432fcd904a43511876df5cdf3e7e548", 64, public_key);
|
from_hex("77ff84905a91936367c01360803104f92432fcd904a43511876df5cdf3e7e548", 64, public_key);
|
||||||
from_hex("e06d3183d14159228433ed599221b80bd0a5ce8352e4bdf0262f76786ef1c74d"
|
from_hex("e06d3183d14159228433ed599221b80bd0a5ce8352e4bdf0262f76786ef1c74d"
|
||||||
"b7e7a9fea2c0eb269d61e3b38e450a22e754941ac78479d6c54e1faf6037881d", 128, private_key);
|
"b7e7a9fea2c0eb269d61e3b38e450a22e754941ac78479d6c54e1faf6037881d", 128, private_key);
|
||||||
|
|
||||||
sign_mutable_item(std::pair<char const*, int>("12:Hello World!", 15), 1
|
sign_mutable_item(test_content, empty_salt, 1, public_key
|
||||||
, public_key, private_key, signature);
|
, private_key, signature);
|
||||||
|
|
||||||
TEST_EQUAL(to_hex(std::string(signature, 64))
|
TEST_EQUAL(to_hex(std::string(signature, 64))
|
||||||
, "305ac8aeb6c9c151fa120f120ea2cfb923564e11552d06a5d856091e5e853cff"
|
, "305ac8aeb6c9c151fa120f120ea2cfb923564e11552d06a5d856091e5e853cff"
|
||||||
"1260d3f39e4999684aa92eb73ffd136e6f4f3ecbfda0ce53a1608ecd7ae21f01");
|
"1260d3f39e4999684aa92eb73ffd136e6f4f3ecbfda0ce53a1608ecd7ae21f01");
|
||||||
|
|
||||||
|
sha1_hash target_id = item_target_id(test_content, empty_salt, public_key);
|
||||||
|
TEST_EQUAL(to_hex(target_id.to_string()), "4a533d47ec9c7d95b1ad75f576cffc641853b750");
|
||||||
|
|
||||||
|
// test vector 2 (the keypair is the same as test 1)
|
||||||
|
|
||||||
|
sign_mutable_item(test_content, test_salt, 1, public_key
|
||||||
|
, private_key, signature);
|
||||||
|
|
||||||
|
TEST_EQUAL(to_hex(std::string(signature, 64))
|
||||||
|
, "6834284b6b24c3204eb2fea824d82f88883a3d95e8b4a21b8c0ded553d17d17d"
|
||||||
|
"df9a8a7104b1258f30bed3787e6cb896fca78c58f8e03b5f18f14951a87d9a08");
|
||||||
|
|
||||||
|
target_id = item_target_id(test_content, test_salt, public_key);
|
||||||
|
TEST_EQUAL(to_hex(target_id.to_string()), "411eba73b6f087ca51a3795d9c8c938d365e32c1");
|
||||||
|
|
||||||
|
// test vector 3
|
||||||
|
|
||||||
|
target_id = item_target_id(test_content, empty_salt, NULL);
|
||||||
|
TEST_EQUAL(to_hex(target_id.to_string()), "e5f96f6f38320f0f33959cb4d3d656452117aadb");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue