#
# patch "AUTHORS"
# from [18efd7462a60094848537d1248f3e668742bfe8e]
# to [1bc23bee8e818ed6bc3b2d173b0f6435cbc92832]
#
# patch "ChangeLog"
# from [098f931c622ef79fe7cc8f389d4851fe9599541f]
# to [f6962ae1e54db8ef7df9d8daf40c08415d3b60d7]
#
# patch "constants.cc"
# from [eabc0d5838a67e3e4d351ce3d28eac0387f773ae]
# to [2258f6165b6db8f8c952f97aaacf0addbbed060f]
#
# patch "constants.hh"
# from [4a4494bcaa97ec07c147fc066564519bb4d13481]
# to [a4741c95ae4faeb5db1f36f6ccf406f732250241]
#
# patch "keys.cc"
# from [a4ccef56d24ee9cda7a009144f5476e3cdcb07d7]
# to [9c50dc0d669ffb6ad2328c46485bf3ae65dad4b5]
#
# patch "keys.hh"
# from [f10aaea8dbaf4005a55ec388f278d1bb81d664af]
# to [ec2da9cbde80341456204b6e0e0b4f727388ed8b]
#
# patch "netcmd.cc"
# from [e04a41fed951a77dcef389e94cc26d04ae12220f]
# to [7308c12cecebbd725ddca9ebfa94bd9a6e1efdea]
#
# patch "netcmd.hh"
# from [5626696805eab24ff5bd23f114a749b3aa78645a]
# to [db259c3bbd1fdc984b3cae3fb90128f7b0e461e5]
#
# patch "netsync.cc"
# from [6ac44712dc4b51df54c5a0ab95bd0ee0211d26eb]
# to [a216c4dc84981cff4cdf26cc45303c46a16054fd]
#
# patch "vocab.cc"
# from [6e07baffa263c80f013d474ad362c3b52c162201]
# to [0a568b1d7a988e146671055888136fdff7818916]
#
# patch "vocab_terms.hh"
# from [c12dd87ed775d3e2d8713a393cc055ce68e5ae16]
# to [fe3bf7a7700bdde82211ba0be39caf248025e00b]
#
--- AUTHORS
+++ AUTHORS
@@ -59,6 +59,7 @@
Timothy Brownawell
Matthew Gregan
Riccardo Ghetta
+ Ethan Blanton
supporting files:
-----------------
--- ChangeLog
+++ ChangeLog
@@ -1,3 +1,69 @@
+2005-06-25 Nathaniel Smith
+
+ * netsync.cc: Tweak comment.
+
+2005-06-25 Nathaniel Smith
+
+ * AUTHORS: Add Ethan Blanton .
+
+2005-06-22 Nathaniel Smith
+
+ * netcmd.hh (netcmd::read, netcmd::write): Don't have defaults for
+ key/hmac arguments.
+ * netcmd.cc (do_netcmd_roundtrip): New function.
+ (test_netcmd_functions): Use it. Also, make work with hmac
+ changes.
+ (test_netcmd_mac): New test.
+ (add_netcmd_tests): Call it.
+
+2005-06-22 Nathaniel Smith
+
+ * netcmd.cc (read): Remove unused variable.
+ * netsync.cc (call_server, process)
+ (arm_sessions_and_calculate_probe, handle_read_available): Give
+ better error message on bad_decode exceptions.
+
+2005-06-22 Nathaniel Smith
+
+ * netcmd.cc, netsync.cc: Revert backwards compatibility code; 0.19
+ and 0.20 can't be usefully compatible, and the code as it existed
+ would cause real version mismatch error reporting to not work
+ right. (Old client with new server would give a generic "server
+ disconnected" error message instead of something useful.)
+
+2005-06-21 Nathaniel Smith
+
+ * netsync.cc (rebuild_merkle_trees): Fix FIXME comments to match
+ reality.
+ * tests/t_netsync_diffbranch.at: No longer a bug, remove
+ priority.
+
+2005-06-20 Nathaniel Smith
+
+ * monotone.texi (Hook Reference): Oops, missed a @ref.
+
+2005-06-20 Nathaniel Smith
+
+ * monotone.texi (Default monotonerc): Rename section to...
+ (Default hooks): ...this, to emphasize is still read even when a
+ monotonerc exists.
+
+2005-06-19 Richard Levitte
+
+ * Makefile.am: There's no reason for monotone.pdf or .dvi to
+ depend on monotone.info, since they are built from the .texi
+ files. Also, make the monotone.html and html targets depend
+ on version.texi and std_hooks.lua as well.
+
+2005-06-18 Matt Johnston
+
+ * INSTALL: fix typo, should be -Iboost_1_31_0 not -Iboost_1_31_2
+
+2005-06-18 Riccardo Ghetta
+ * monotone.texi: include std_hooks.lua as an appendix and remove long
+ lua excerpts from hook reference.
+ * Makefile.am : make monotone.pdf/eps depend on monotone.info
+
2005-06-24 Matt Johnston
* transforms.{cc,hh}: combine gzip and base64 in one
--- constants.cc
+++ constants.cc
@@ -160,5 +160,9 @@
size_t const netsync_default_port = 5253;
size_t const netsync_connection_limit = 1024;
size_t const netsync_timeout_seconds = 21600; // 6 hours
+ size_t const netsync_session_key_length_in_bytes = 20; // 160 bits
+ size_t const netsync_hmac_value_length_in_bytes = 20; // 160 bits
+ std::string const & netsync_key_initializer = std::string(netsync_session_key_length_in_bytes, 0);
+
}
--- constants.hh
+++ constants.hh
@@ -7,6 +7,7 @@
// see the file COPYING for details
#include
+#include
#include "numeric_vocab.hh"
namespace constants
@@ -120,6 +121,15 @@
// number of seconds a connection can be idle before it's dropped
extern size_t const netsync_timeout_seconds;
+ // netsync HMAC key length
+ extern size_t const netsync_session_key_length_in_bytes;
+
+ // netsync HMAC value length
+ extern size_t const netsync_hmac_value_length_in_bytes;
+
+ // netsync session key and HMAC key default initializer
+ extern std::string const & netsync_key_initializer;
+
}
#endif // __CONSTANTS_HH__
--- keys.cc
+++ keys.cc
@@ -166,7 +166,21 @@
der_encoded[i] = '\0';
}
+static bool
+blocking_rng(lua_hooks & lua)
+{
+ if (!lua.hook_non_blocking_rng_ok())
+ {
+#ifndef BLOCKING_RNG_AVAILABLE
+ throw oops("no blocking RNG available and non-blocking RNG rejected");
+#else
+ return true;
+#endif
+ };
+ return false;
+}
+
void
generate_key_pair(lua_hooks & lua, // to hook for phrase
rsa_keypair_id const & id, // to prompting user for phrase
@@ -176,17 +190,7 @@
{
// we will panic here if the user doesn't like urandom and we can't give
// them a real entropy-driven random.
- bool request_blocking_rng = false;
- if (!lua.hook_non_blocking_rng_ok())
- {
-#ifndef BLOCKING_RNG_AVAILABLE
- throw oops("no blocking RNG available and non-blocking RNG rejected");
-#else
- request_blocking_rng = true;
-#endif
- }
-
- AutoSeededRandomPool rng(request_blocking_rng);
+ AutoSeededRandomPool rng(blocking_rng(lua));
SecByteBlock phrase, pubkey, privkey;
rsa_pub_key raw_pub_key;
arc4 raw_priv_key;
@@ -267,16 +271,7 @@
// we will panic here if the user doesn't like urandom and we can't give
// them a real entropy-driven random.
- bool request_blocking_rng = false;
- if (!lua.hook_non_blocking_rng_ok())
- {
-#ifndef BLOCKING_RNG_AVAILABLE
- throw oops("no blocking RNG available and non-blocking RNG rejected");
-#else
- request_blocking_rng = true;
-#endif
- }
- AutoSeededRandomPool rng(request_blocking_rng);
+ AutoSeededRandomPool rng(blocking_rng(lua));
// we permit the user to relax security here, by caching a decrypted key
// (if they permit it) through the life of a program run. this helps when
@@ -396,6 +391,75 @@
return vf->GetLastResult();
}
+void encrypt_rsa(lua_hooks & lua,
+ rsa_keypair_id const & id,
+ base64 & pub_encoded,
+ std::string const & plaintext,
+ rsa_oaep_sha_data & ciphertext)
+{
+ AutoSeededRandomPool rng(blocking_rng(lua));
+
+ rsa_pub_key pub;
+ decode_base64(pub_encoded, pub);
+ SecByteBlock pub_block;
+ pub_block.Assign(reinterpret_cast(pub().data()), pub().size());
+ StringSource keysource(pub_block.data(), pub_block.size(), true);
+
+ shared_ptr encryptor;
+ encryptor = shared_ptr
+ (new RSAES_OAEP_SHA_Encryptor(keysource));
+
+ string ciphertext_string;
+ StringSource tmp(plaintext, true,
+ encryptor->CreateEncryptionFilter
+ (rng, new StringSink(ciphertext_string)));
+
+ ciphertext = rsa_oaep_sha_data(ciphertext_string);
+}
+
+void decrypt_rsa(lua_hooks & lua,
+ rsa_keypair_id const & id,
+ base64< arc4 > const & priv,
+ rsa_oaep_sha_data const & ciphertext,
+ std::string & plaintext)
+{
+ AutoSeededRandomPool rng(blocking_rng(lua));
+ arc4 decoded_key;
+ SecByteBlock decrypted_key;
+ SecByteBlock phrase;
+ shared_ptr decryptor;
+
+ for (int i = 0; i < 3; i++)
+ {
+ bool force = false;
+ decode_base64(priv, decoded_key);
+ decrypted_key.Assign(reinterpret_cast(decoded_key().data()),
+ decoded_key().size());
+ get_passphrase(lua, id, phrase, false, force);
+
+ try
+ {
+ do_arc4(phrase, decrypted_key);
+ StringSource keysource(decrypted_key.data(), decrypted_key.size(), true);
+ decryptor = shared_ptr
+ (new RSAES_OAEP_SHA_Decryptor(keysource));
+ }
+ catch (...)
+ {
+ if (i >= 2)
+ throw informative_failure("failed to decrypt private RSA key, "
+ "probably incorrect passphrase");
+ // don't use the cache bad one next time
+ force = true;
+ continue;
+ }
+ }
+
+ StringSource tmp(ciphertext(), true,
+ decryptor->CreateDecryptionFilter
+ (rng, new StringSink(plaintext)));
+}
+
void
read_pubkey(string const & in,
rsa_keypair_id & id,
--- keys.hh
+++ keys.hh
@@ -42,6 +42,18 @@
void require_password(rsa_keypair_id const & id,
app_state & app);
+void encrypt_rsa(lua_hooks & lua,
+ rsa_keypair_id const & id,
+ base64 & pub,
+ std::string const & plaintext,
+ rsa_oaep_sha_data & ciphertext);
+
+void decrypt_rsa(lua_hooks & lua,
+ rsa_keypair_id const & id,
+ base64< arc4 > const & priv,
+ rsa_oaep_sha_data const & ciphertext,
+ std::string & plaintext);
+
// netsync stuff
void read_pubkey(std::string const & in,
--- netcmd.cc
+++ netcmd.cc
@@ -8,6 +8,8 @@
#include
#include "cryptopp/gzip.h"
+#include "cryptopp/hmac.h"
+#include "cryptopp/sha.h"
#include "adler32.hh"
#include "constants.hh"
@@ -50,9 +52,6 @@
cmd_code(bye_cmd)
{}
-netcmd::netcmd(u8 _version) : version(_version), cmd_code(bye_cmd)
-{}
-
size_t netcmd::encoded_size()
{
string tmp;
@@ -69,61 +68,53 @@
}
void
-netcmd::write(string & out) const
+netcmd::write(string & out, netsync_session_key const & key,
+ netsync_hmac_value & hmac_val) const
{
+ size_t oldlen = out.size();
out += static_cast(version);
out += static_cast(cmd_code);
insert_variable_length_string(payload, out);
- adler32 check(reinterpret_cast(payload.data()), payload.size());
- insert_datum_lsb(check.sum(), out);
-}
-// last should be zero (doesn't mean we're compatible with version 0).
-// The nonzero elements are the historical netsync/netcmd versions we can
-// interoperate with. For interoperating with newer versions, assume
-// compatibility and let the remote host make the call.
-static u8 const compatible_versions[] = {4, 0};
-
-bool is_compatible(u8 version)
-{
- for (u8 const *x = compatible_versions; *x; ++x)
+ I(key().size() == CryptoPP::SHA::DIGESTSIZE);
+ I(key().size() == hmac_val().size());
+ byte keybuf[CryptoPP::SHA::DIGESTSIZE];
+ for (size_t i = 0; i < sizeof(keybuf); i++)
{
- if (*x == version)
- return true;
+ keybuf[i] = key()[i] ^ hmac_val()[i];
}
- return false;
+ CryptoPP::HMAC hmac(keybuf, sizeof(keybuf));
+ char digest[CryptoPP::SHA::DIGESTSIZE];
+ hmac.CalculateDigest(reinterpret_cast(digest),
+ reinterpret_cast(out.data() + oldlen),
+ out.size() - oldlen);
+ string digest_str(digest, sizeof(digest));
+ hmac_val = netsync_hmac_value(digest_str);
+ out.append(digest_str);
}
-
+
bool
-netcmd::read(string & inbuf)
+netcmd::read(string & inbuf, netsync_session_key const & key,
+ netsync_hmac_value & hmac_val)
{
size_t pos = 0;
if (inbuf.size() < constants::netcmd_minsz)
return false;
- u8 ver = extract_datum_lsb(inbuf, pos, "netcmd protocol number");
- int v = version;
+ u8 extracted_ver = extract_datum_lsb(inbuf, pos, "netcmd protocol number");
+ if (extracted_ver != version)
+ throw bad_decode(F("protocol version mismatch: wanted '%d' got '%d'")
+ % widen(version)
+ % widen(extracted_ver));
+ version = extracted_ver;
u8 cmd_byte = extract_datum_lsb(inbuf, pos, "netcmd code");
switch (cmd_byte)
{
- // hello may be newer than expected, or one we're compatible with
case static_cast(hello_cmd):
- if (ver < version && !is_compatible(ver))
- throw bad_decode(F("protocol version mismatch: wanted '%d' got '%d'")
- % widen(v)
- % widen(ver));
- break;
- // these may be older compatible versions
case static_cast(anonymous_cmd):
case static_cast(auth_cmd):
- if (ver != version && (ver > version || !is_compatible(ver)))
- throw bad_decode(F("protocol version mismatch: wanted '%d' got '%d'")
- % widen(v)
- % widen(ver));
- break;
- // these must match exactly what's expected
case static_cast(error_cmd):
case static_cast(bye_cmd):
case static_cast(confirm_cmd):
@@ -134,16 +125,11 @@
case static_cast(data_cmd):
case static_cast(delta_cmd):
case static_cast(nonexistant_cmd):
- if (ver != version)
- throw bad_decode(F("protocol version mismatch: wanted '%d' got '%d'")
- % widen(v)
- % widen(ver));
+ cmd_code = static_cast(cmd_byte);
break;
default:
throw bad_decode(F("unknown netcmd code 0x%x") % widen(cmd_byte));
}
- cmd_code = static_cast(cmd_byte);
- version = ver;
// check to see if we have even enough bytes for a complete uleb128
size_t payload_len = 0;
@@ -156,28 +142,43 @@
throw bad_decode(F("oversized payload of '%d' bytes") % payload_len);
// there might not be enough data yet in the input buffer
- if (inbuf.size() < pos + payload_len + sizeof(u32))
+ if (inbuf.size() < pos + payload_len + CryptoPP::SHA::DIGESTSIZE)
{
- inbuf.reserve(pos + payload_len + sizeof(u32) + constants::bufsz);
+ inbuf.reserve(pos + payload_len + CryptoPP::SHA::DIGESTSIZE + constants::bufsz);
return false;
}
// out.payload = extract_substring(inbuf, pos, payload_len, "netcmd payload");
// Do this ourselves, so we can swap the strings instead of copying.
require_bytes(inbuf, pos, payload_len, "netcmd payload");
- inbuf.erase(0, pos);
- payload = inbuf.substr(payload_len);
- inbuf.erase(payload_len, inbuf.npos);
+ payload = inbuf.substr(pos + payload_len);
+ inbuf.erase(pos + payload_len, inbuf.npos);
inbuf.swap(payload);
+ size_t payload_pos = pos;
pos = 0;
// they might have given us bogus data
- u32 checksum = extract_datum_lsb(inbuf, pos, "netcmd checksum");
+ string cmd_digest = extract_substring(inbuf, pos, CryptoPP::SHA::DIGESTSIZE,
+ "netcmd HMAC");
inbuf.erase(0, pos);
- adler32 check(reinterpret_cast(payload.data()),
- payload.size());
- if (checksum != check.sum())
- throw bad_decode(F("bad checksum 0x%x vs. 0x%x") % checksum % check.sum());
+ I(key().size() == CryptoPP::SHA::DIGESTSIZE);
+ I(key().size() == hmac_val().size());
+ byte keybuf[CryptoPP::SHA::DIGESTSIZE];
+ for (size_t i = 0; i < sizeof(keybuf); i++)
+ {
+ keybuf[i] = key()[i] ^ hmac_val()[i];
+ }
+ CryptoPP::HMAC hmac(keybuf, sizeof(keybuf));
+ char digest_buf[CryptoPP::SHA::DIGESTSIZE];
+ hmac.CalculateDigest(reinterpret_cast(digest_buf),
+ reinterpret_cast(payload.data()),
+ payload.size());
+ string digest(digest_buf, sizeof(digest_buf));
+ if (cmd_digest != digest)
+ throw bad_decode(F("bad HMAC %s vs. %s") % encode_hexenc(cmd_digest)
+ % encode_hexenc(digest));
+ hmac_val = netsync_hmac_value(digest);
+ payload.erase(0, payload_pos);
return true;
}
@@ -238,37 +239,37 @@
}
-void
-netcmd::read_anonymous_cmd(protocol_role & role,
+void
+netcmd::read_anonymous_cmd(protocol_role & role,
std::string & pattern,
- id & nonce2) const
+ rsa_oaep_sha_data & hmac_key_encrypted) const
{
size_t pos = 0;
- // syntax is:
- u8 role_byte = extract_datum_lsb(payload, pos, "anonymous netcmd, role");
+ // syntax is:
+ u8 role_byte = extract_datum_lsb(payload, pos, "anonymous(hmac) netcmd, role");
if (role_byte != static_cast(source_role)
&& role_byte != static_cast(sink_role)
&& role_byte != static_cast(source_and_sink_role))
throw bad_decode(F("unknown role specifier %d") % widen(role_byte));
role = static_cast(role_byte);
extract_variable_length_string(payload, pattern, pos,
- "anonymous netcmd, pattern");
- nonce2 = id(extract_substring(payload, pos,
- constants::merkle_hash_length_in_bytes,
- "anonymous netcmd, nonce2"));
- assert_end_of_buffer(payload, pos, "anonymous netcmd payload");
+ "anonymous(hmac) netcmd, pattern");
+ string hmac_key_string;
+ extract_variable_length_string(payload, hmac_key_string, pos,
+ "anonymous(hmac) netcmd, hmac_key_encrypted");
+ hmac_key_encrypted = rsa_oaep_sha_data(hmac_key_string);
+ assert_end_of_buffer(payload, pos, "anonymous(hmac) netcmd payload");
}
-void
-netcmd::write_anonymous_cmd(protocol_role role,
+void
+netcmd::write_anonymous_cmd(protocol_role role,
std::string const & pattern,
- id const & nonce2)
+ rsa_oaep_sha_data const & hmac_key_encrypted)
{
cmd_code = anonymous_cmd;
- I(nonce2().size() == constants::merkle_hash_length_in_bytes);
payload = static_cast(role);
insert_variable_length_string(pattern, payload);
- payload += nonce2();
+ insert_variable_length_string(hmac_key_encrypted(), payload);
}
void
@@ -276,74 +277,68 @@
string & pattern,
id & client,
id & nonce1,
- id & nonce2,
+ rsa_oaep_sha_data & hmac_key_encrypted,
string & signature) const
{
size_t pos = 0;
// syntax is:
- //
- //
+ //
+ //
u8 role_byte = extract_datum_lsb(payload, pos, "auth netcmd, role");
if (role_byte != static_cast(source_role)
&& role_byte != static_cast(sink_role)
&& role_byte != static_cast(source_and_sink_role))
throw bad_decode(F("unknown role specifier %d") % widen(role_byte));
role = static_cast(role_byte);
- extract_variable_length_string(payload, pattern, pos, "auth netcmd, pattern");
+ extract_variable_length_string(payload, pattern, pos, "auth(hmac) netcmd, pattern");
client = id(extract_substring(payload, pos,
constants::merkle_hash_length_in_bytes,
- "auth netcmd, client identifier"));
+ "auth(hmac) netcmd, client identifier"));
nonce1 = id(extract_substring(payload, pos,
constants::merkle_hash_length_in_bytes,
- "auth netcmd, nonce1"));
- nonce2 = id(extract_substring(payload, pos,
- constants::merkle_hash_length_in_bytes,
- "auth netcmd, nonce2"));
+ "auth(hmac) netcmd, nonce1"));
+ string hmac_key;
+ extract_variable_length_string(payload, hmac_key, pos,
+ "auth(hmac) netcmd, hmac_key_encrypted");
+ hmac_key_encrypted = rsa_oaep_sha_data(hmac_key);
extract_variable_length_string(payload, signature, pos,
- "auth netcmd, signature");
- assert_end_of_buffer(payload, pos, "auth netcmd payload");
+ "auth(hmac) netcmd, signature");
+ assert_end_of_buffer(payload, pos, "auth(hmac) netcmd payload");
}
-void
-netcmd::write_auth_cmd(protocol_role role,
- string const & pattern,
+void
+netcmd::write_auth_cmd(protocol_role role,
+ string const & pattern,
id const & client,
- id const & nonce1,
- id const & nonce2,
+ id const & nonce1,
+ rsa_oaep_sha_data const & hmac_key_encrypted,
string const & signature)
{
cmd_code = auth_cmd;
I(client().size() == constants::merkle_hash_length_in_bytes);
I(nonce1().size() == constants::merkle_hash_length_in_bytes);
- I(nonce2().size() == constants::merkle_hash_length_in_bytes);
payload = static_cast(role);
insert_variable_length_string(pattern, payload);
payload += client();
payload += nonce1();
- payload += nonce2();
+ insert_variable_length_string(hmac_key_encrypted(), payload);
insert_variable_length_string(signature, payload);
}
-
-void
-netcmd::read_confirm_cmd(string & signature) const
+void
+netcmd::read_confirm_cmd() const
{
size_t pos = 0;
-
- // syntax is:
- extract_variable_length_string(payload, signature, pos,
- "confirm netcmd, signature");
assert_end_of_buffer(payload, pos, "confirm netcmd payload");
}
-void
-netcmd::write_confirm_cmd(string const & signature)
+void
+netcmd::write_confirm_cmd()
{
cmd_code = confirm_cmd;
payload.clear();
- insert_variable_length_string(signature, payload);
}
-
+
void
netcmd::read_refine_cmd(merkle_node & node) const
{
@@ -559,6 +554,65 @@
#include "transforms.hh"
#include
+void
+test_netcmd_mac()
+{
+ netcmd out_cmd, in_cmd;
+ string buf;
+ netsync_session_key key(constants::netsync_key_initializer);
+ {
+ netsync_hmac_value mac(constants::netsync_key_initializer);
+ // mutates mac
+ out_cmd.write(buf, key, mac);
+ BOOST_CHECK_THROW(in_cmd.read(buf, key, mac), bad_decode);
+ }
+
+ {
+ netsync_hmac_value mac(constants::netsync_key_initializer);
+ out_cmd.write(buf, key, mac);
+ }
+ buf[0] ^= 0xff;
+ {
+ netsync_hmac_value mac(constants::netsync_key_initializer);
+ BOOST_CHECK_THROW(in_cmd.read(buf, key, mac), bad_decode);
+ }
+
+ {
+ netsync_hmac_value mac(constants::netsync_key_initializer);
+ out_cmd.write(buf, key, mac);
+ }
+ buf[buf.size() - 1] ^= 0xff;
+ {
+ netsync_hmac_value mac(constants::netsync_key_initializer);
+ BOOST_CHECK_THROW(in_cmd.read(buf, key, mac), bad_decode);
+ }
+
+ {
+ netsync_hmac_value mac(constants::netsync_key_initializer);
+ out_cmd.write(buf, key, mac);
+ }
+ buf += '\0';
+ {
+ netsync_hmac_value mac(constants::netsync_key_initializer);
+ BOOST_CHECK_THROW(in_cmd.read(buf, key, mac), bad_decode);
+ }
+}
+
+static void
+do_netcmd_roundtrip(netcmd const & out_cmd, netcmd & in_cmd, string & buf)
+{
+ netsync_session_key key(constants::netsync_key_initializer);
+ {
+ netsync_hmac_value mac(constants::netsync_key_initializer);
+ out_cmd.write(buf, key, mac);
+ }
+ {
+ netsync_hmac_value mac(constants::netsync_key_initializer);
+ BOOST_CHECK(in_cmd.read(buf, key, mac));
+ }
+ BOOST_CHECK(in_cmd == out_cmd);
+}
+
void
test_netcmd_functions()
{
@@ -573,10 +627,8 @@
string out_errmsg("your shoelaces are untied"), in_errmsg;
string buf;
out_cmd.write_error_cmd(out_errmsg);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_error_cmd(in_errmsg);
- BOOST_CHECK(in_cmd == out_cmd);
BOOST_CHECK(in_errmsg == out_errmsg);
L(F("errmsg_cmd test done, buffer was %d bytes\n") % buf.size());
}
@@ -586,9 +638,7 @@
L(F("checking i/o round trip on bye_cmd\n"));
netcmd out_cmd, in_cmd;
string buf;
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
- BOOST_CHECK(in_cmd == out_cmd);
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
L(F("bye_cmd test done, buffer was %d bytes\n") % buf.size());
}
@@ -601,10 +651,8 @@
rsa_pub_key out_server_key("9387938749238792874"), in_server_key;
id out_nonce(raw_sha1("nonce it up")), in_nonce;
out_cmd.write_hello_cmd(out_server_keyname, out_server_key, out_nonce);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_hello_cmd(in_server_keyname, in_server_key, in_nonce);
- BOOST_CHECK(in_cmd == out_cmd);
BOOST_CHECK(in_server_keyname == out_server_keyname);
BOOST_CHECK(in_server_key == out_server_key);
BOOST_CHECK(in_nonce == out_nonce);
@@ -617,15 +665,15 @@
netcmd out_cmd, in_cmd;
protocol_role out_role = source_and_sink_role, in_role;
string buf;
- id out_nonce2(raw_sha1("nonce start my heart")), in_nonce2;
+ // total cheat, since we don't actually verify that rsa_oaep_sha_data
+ // is sensible anywhere here...
+ rsa_oaep_sha_data out_key("nonce start my heart"), in_key;
string out_pattern("radishes galore!"), in_pattern;
- out_cmd.write_anonymous_cmd(out_role, out_pattern, out_nonce2);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
- in_cmd.read_anonymous_cmd(in_role, in_pattern, in_nonce2);
- BOOST_CHECK(in_cmd == out_cmd);
- BOOST_CHECK(in_nonce2 == out_nonce2);
+ out_cmd.write_anonymous_cmd(out_role, out_pattern, out_key);
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
+ in_cmd.read_anonymous_cmd(in_role, in_pattern, in_key);
+ BOOST_CHECK(in_key == out_key);
BOOST_CHECK(in_role == out_role);
L(F("anonymous_cmd test done, buffer was %d bytes\n") % buf.size());
}
@@ -637,21 +685,21 @@
protocol_role out_role = source_and_sink_role, in_role;
string buf;
id out_client(raw_sha1("happy client day")), out_nonce1(raw_sha1("nonce me amadeus")),
- out_nonce2(raw_sha1("nonce start my heart")),
- in_client, in_nonce1, in_nonce2;
+ in_client, in_nonce1;
+ // total cheat, since we don't actually verify that rsa_oaep_sha_data
+ // is sensible anywhere here...
+ rsa_oaep_sha_data out_key("nonce start my heart"), in_key;
string out_signature(raw_sha1("burble") + raw_sha1("gorby")), out_pattern("radishes galore!"),
in_signature, in_pattern;
out_cmd.write_auth_cmd(out_role, out_pattern, out_client, out_nonce1,
- out_nonce2, out_signature);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ out_key, out_signature);
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_auth_cmd(in_role, in_pattern, in_client,
- in_nonce1, in_nonce2, in_signature);
- BOOST_CHECK(in_cmd == out_cmd);
+ in_nonce1, in_key, in_signature);
BOOST_CHECK(in_client == out_client);
BOOST_CHECK(in_nonce1 == out_nonce1);
- BOOST_CHECK(in_nonce2 == out_nonce2);
+ BOOST_CHECK(in_key == out_key);
BOOST_CHECK(in_signature == out_signature);
BOOST_CHECK(in_role == out_role);
L(F("auth_cmd test done, buffer was %d bytes\n") % buf.size());
@@ -662,14 +710,9 @@
L(F("checking i/o round trip on confirm_cmd\n"));
netcmd out_cmd, in_cmd;
string buf;
- string out_signature(raw_sha1("egg") + raw_sha1("tomago")), in_signature;
-
- out_cmd.write_confirm_cmd(out_signature);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
- in_cmd.read_confirm_cmd(in_signature);
- BOOST_CHECK(in_cmd == out_cmd);
- BOOST_CHECK(in_signature == out_signature);
+ out_cmd.write_confirm_cmd();
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
+ in_cmd.read_confirm_cmd();
L(F("confirm_cmd test done, buffer was %d bytes\n") % buf.size());
}
@@ -690,10 +733,8 @@
out_node.set_slot_state(15, subtree_state);
out_cmd.write_refine_cmd(out_node);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_refine_cmd(in_node);
- BOOST_CHECK(in_cmd == out_cmd);
BOOST_CHECK(in_node == out_node);
L(F("refine_cmd test done, buffer was %d bytes\n") % buf.size());
}
@@ -707,8 +748,7 @@
string buf;
out_cmd.write_done_cmd(out_level, out_type);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_done_cmd(in_level, in_type);
BOOST_CHECK(in_level == out_level);
BOOST_CHECK(in_type == out_type);
@@ -724,8 +764,7 @@
string buf;
out_cmd.write_send_data_cmd(out_type, out_id);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_send_data_cmd(in_type, in_id);
BOOST_CHECK(in_type == out_type);
BOOST_CHECK(in_id == out_id);
@@ -742,8 +781,7 @@
string buf;
out_cmd.write_send_delta_cmd(out_type, out_head, out_base);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_send_delta_cmd(in_type, in_head, in_base);
BOOST_CHECK(in_type == out_type);
BOOST_CHECK(in_head == out_head);
@@ -760,8 +798,7 @@
string out_dat("thank you for flying northwest"), in_dat;
string buf;
out_cmd.write_data_cmd(out_type, out_id, out_dat);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_data_cmd(in_type, in_id, in_dat);
BOOST_CHECK(in_id == out_id);
BOOST_CHECK(in_dat == out_dat);
@@ -779,8 +816,7 @@
string buf;
out_cmd.write_delta_cmd(out_type, out_head, out_base, out_delta);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_delta_cmd(in_type, in_head, in_base, in_delta);
BOOST_CHECK(in_type == out_type);
BOOST_CHECK(in_head == out_head);
@@ -798,8 +834,7 @@
string buf;
out_cmd.write_nonexistant_cmd(out_type, out_id);
- out_cmd.write(buf);
- BOOST_CHECK(in_cmd.read(buf));
+ do_netcmd_roundtrip(out_cmd, in_cmd, buf);
in_cmd.read_nonexistant_cmd(in_type, in_id);
BOOST_CHECK(in_type == out_type);
BOOST_CHECK(in_id == out_id);
@@ -818,6 +853,7 @@
add_netcmd_tests(test_suite * suite)
{
suite->add(BOOST_TEST_CASE(&test_netcmd_functions));
+ suite->add(BOOST_TEST_CASE(&test_netcmd_mac));
}
#endif // BUILD_UNIT_TESTS
--- netcmd.hh
+++ netcmd.hh
@@ -62,8 +62,11 @@
// basic cmd i/o (including checksums)
- void write(std::string & out) const;
- bool read(std::string & inbuf);
+ void write(std::string & out,
+ netsync_session_key const & key,
+ netsync_hmac_value & hmac_val) const;
+ bool read(std::string & inbuf,
+ netsync_session_key const & key, netsync_hmac_value & hmac_val);
// i/o functions for each type of command payload
void read_error_cmd(std::string & errmsg) const;
@@ -79,28 +82,28 @@
rsa_pub_key const & server_key,
id const & nonce);
- void read_anonymous_cmd(protocol_role & role,
+ void read_anonymous_cmd(protocol_role & role,
std::string & pattern,
- id & nonce2) const;
+ rsa_oaep_sha_data & hmac_key_encrypted) const;
void write_anonymous_cmd(protocol_role role,
std::string const & pattern,
- id const & nonce2);
+ rsa_oaep_sha_data const & hmac_key_encrypted);
void read_auth_cmd(protocol_role & role,
std::string & pattern,
id & client,
id & nonce1,
- id & nonce2,
+ rsa_oaep_sha_data & hmac_key_encrypted,
std::string & signature) const;
void write_auth_cmd(protocol_role role,
std::string const & pattern,
id const & client,
id const & nonce1,
- id const & nonce2,
+ rsa_oaep_sha_data const & hmac_key_encrypted,
std::string const & signature);
- void read_confirm_cmd(std::string & signature) const;
- void write_confirm_cmd(std::string const & signature);
+ void read_confirm_cmd() const;
+ void write_confirm_cmd();
void read_refine_cmd(merkle_node & node) const;
void write_refine_cmd(merkle_node const & node);
--- netsync.cc
+++ netsync.cc
@@ -112,18 +112,33 @@
// protocol
// --------
//
+// The protocol is a simple binary command-packet system over TCP;
+// each packet consists of a single byte which identifies the protocol
+// version, a byte which identifies the command name inside that
+// version, a size_t sent as a uleb128 indicating the length of the
+// packet, that many bytes of payload, and finally 20 bytes of SHA-1
+// HMAC calculated over the payload. The key for the SHA-1 HMAC is 20
+// bytes of 0 during authentication, and a 20-byte random key chosen
+// by the client after authentication (discussed below).
+//
+//---- Pre-v5 packet format ----
+//
// the protocol is a simple binary command-packet system over tcp; each
// packet consists of a byte which identifies the protocol version, a byte
// which identifies the command name inside that version, a size_t sent as
// a uleb128 indicating the length of the packet, and then that many bytes
// of payload, and finally 4 bytes of adler32 checksum (in LSB order) over
-// the payload. decoding involves simply buffering until a sufficient
-// number of bytes are received, then advancing the buffer pointer. any
-// time an adler32 check fails, the protocol is assumed to have lost
-// synchronization, and the connection is dropped. the parties are free to
-// drop the tcp stream at any point, if too much data is received or too
-// much idle time passes; no commitments or transactions are made.
+// the payload.
//
+// ---- end pre-v5 packet format ----
+//
+// decoding involves simply buffering until a sufficient number of bytes are
+// received, then advancing the buffer pointer. any time an integrity check
+// (the HMAC) fails, the protocol is assumed to have lost synchronization, and
+// the connection is dropped. the parties are free to drop the tcp stream at
+// any point, if too much data is received or too much idle time passes; no
+// commitments or transactions are made.
+//
// one special command, "bye", is used to shut down a connection
// gracefully. once each side has received all the data they want, they
// can send a "bye" command to the other side. as soon as either side has
@@ -135,6 +150,31 @@
// "hello " command, which identifies the server's RSA key and
// issues a nonce which must be used for a subsequent authentication.
//
+// The client then responds with either:
+//
+// An "auth (source|sink|both)
+// " command, which identifies its RSA key, notes the role it
+// wishes to play in the synchronization, identifies the pattern it
+// wishes to sync with, signs the previous nonce with its own key, and
+// informs the server of the HMAC key it wishes to use for this
+// session (encrypted with the server's public key); or
+//
+// An "anonymous (source|sink|both) " command,
+// which identifies the role it wishes to play in the synchronization,
+// the pattern it ishes to sync with, and the HMAC key it wishes to
+// use for this session (also encrypted with the server's public key).
+//
+// The server then replies with a "confirm" command, which contains no
+// other data but will only have the correct HMAC integrity code if
+// the server received and properly decrypted the HMAC key offered by
+// the client. This transitions the peers into an authenticated state
+// and begins refinement.
+//
+// ---- Pre-v5 authentication process notes ----
+//
+// the exchange begins in a non-authenticated state. the server sends a
+// "hello " command, which identifies the server's RSA key and
+// issues a nonce which must be used for a subsequent authentication.
// the client can then respond with an "auth (source|sink|both)
// " command which identifies its
// RSA key, notes the role it wishes to play in the synchronization,
@@ -146,6 +186,8 @@
// the signature of the second nonce sent by the client. this
// transitions the peers into an authenticated state and begins refinement.
//
+// ---- End pre-v5 authentication process ----
+//
// refinement begins with the client sending its root public key and
// manifest certificate merkle nodes to the server. the server then
// compares the root to each slot in *its* root node, and for each slot
@@ -221,7 +263,6 @@
string outbuf;
netcmd cmd;
- u8 protocol_version;
bool armed;
bool arm();
@@ -229,6 +270,9 @@
boost::regex pattern_re;
id remote_peer_key_hash;
rsa_keypair_id remote_peer_key_name;
+ netsync_session_key session_key;
+ netsync_hmac_value read_hmac;
+ netsync_hmac_value write_hmac;
bool authenticated;
time_t last_io_time;
@@ -321,14 +365,16 @@
id const & nonce);
void queue_anonymous_cmd(protocol_role role,
string const & pattern,
- id const & nonce2);
+ id const & nonce2,
+ base64 server_key_encoded);
void queue_auth_cmd(protocol_role role,
string const & pattern,
id const & client,
id const & nonce1,
id const & nonce2,
- string const & signature);
- void queue_confirm_cmd(string const & signature);
+ string const & signature,
+ base64 server_key_encoded);
+ void queue_confirm_cmd();
void queue_refine_cmd(merkle_node const & node);
void queue_send_data_cmd(netcmd_item_type type,
id const & item);
@@ -352,15 +398,15 @@
rsa_pub_key const & server_key,
id const & nonce);
bool process_anonymous_cmd(protocol_role role,
- string const & pattern,
- id const & nonce2);
+ string const & pattern);
bool process_auth_cmd(protocol_role role,
string const & pattern,
id const & client,
id const & nonce1,
- id const & nonce2,
string const & signature);
+ void respond_to_auth_cmd(rsa_oaep_sha_data hmac_key_encrypted);
bool process_confirm_cmd(string const & signature);
+ void respond_to_confirm_cmd();
bool process_refine_cmd(merkle_node const & node);
bool process_send_data_cmd(netcmd_item_type type,
id const & item);
@@ -433,12 +479,14 @@
str(sock, to),
inbuf(""),
outbuf(""),
- protocol_version(constants::netcmd_current_protocol_version),
armed(false),
pattern(""),
pattern_re(".*"),
remote_peer_key_hash(""),
remote_peer_key_name(""),
+ session_key(constants::netsync_key_initializer),
+ read_hmac(constants::netsync_key_initializer),
+ write_hmac(constants::netsync_key_initializer),
authenticated(false),
last_io_time(::time(NULL)),
byte_in_ticker(NULL),
@@ -729,7 +777,7 @@
session::write_netcmd_and_try_flush(netcmd const & cmd)
{
if (!encountered_error)
- cmd.write(outbuf);
+ cmd.write(outbuf, session_key, write_hmac);
else
L(F("dropping outgoing netcmd (because we're in error unwind mode)\n"));
// FIXME: this helps keep the protocol pipeline full but it seems to
@@ -1316,7 +1364,7 @@
session::queue_bye_cmd()
{
L(F("queueing 'bye' command\n"));
- netcmd cmd(protocol_version);
+ netcmd cmd;
cmd.write_bye_cmd();
write_netcmd_and_try_flush(cmd);
this->sent_goodbye = true;
@@ -1326,7 +1374,7 @@
session::queue_error_cmd(string const & errmsg)
{
L(F("queueing 'error' command\n"));
- netcmd cmd(protocol_version);
+ netcmd cmd;
cmd.write_error_cmd(errmsg);
write_netcmd_and_try_flush(cmd);
this->sent_goodbye = true;
@@ -1339,7 +1387,7 @@
string typestr;
netcmd_item_type_to_string(type, typestr);
L(F("queueing 'done' command for %s level %s\n") % typestr % level);
- netcmd cmd(protocol_version);
+ netcmd cmd;
cmd.write_done_cmd(level, type);
write_netcmd_and_try_flush(cmd);
}
@@ -1348,7 +1396,7 @@
session::queue_hello_cmd(id const & server,
id const & nonce)
{
- netcmd cmd(protocol_version);
+ netcmd cmd;
hexenc server_encoded;
encode_hexenc(server, server_encoded);
@@ -1365,11 +1413,16 @@
void
session::queue_anonymous_cmd(protocol_role role,
string const & pattern,
- id const & nonce2)
+ id const & nonce2,
+ base64 server_key_encoded)
{
- netcmd cmd(protocol_version);
- cmd.write_anonymous_cmd(role, pattern, nonce2);
+ netcmd cmd;
+ rsa_oaep_sha_data hmac_key_encrypted;
+ encrypt_rsa(app.lua, remote_peer_key_name, server_key_encoded,
+ nonce2(), hmac_key_encrypted);
+ cmd.write_anonymous_cmd(role, pattern, hmac_key_encrypted);
write_netcmd_and_try_flush(cmd);
+ session_key = netsync_session_key(nonce2());
}
void
@@ -1378,18 +1431,23 @@
id const & client,
id const & nonce1,
id const & nonce2,
- string const & signature)
+ string const & signature,
+ base64 server_key_encoded)
{
- netcmd cmd(protocol_version);
- cmd.write_auth_cmd(role, pattern, client, nonce1, nonce2, signature);
+ netcmd cmd;
+ rsa_oaep_sha_data hmac_key_encrypted;
+ encrypt_rsa(app.lua, remote_peer_key_name, server_key_encoded,
+ nonce2(), hmac_key_encrypted);
+ cmd.write_auth_cmd(role, pattern, client, nonce1, hmac_key_encrypted, signature);
write_netcmd_and_try_flush(cmd);
+ session_key = netsync_session_key(nonce2());
}
-void
-session::queue_confirm_cmd(string const & signature)
+void
+session::queue_confirm_cmd()
{
- netcmd cmd(protocol_version);
- cmd.write_confirm_cmd(signature);
+ netcmd cmd;
+ cmd.write_confirm_cmd();
write_netcmd_and_try_flush(cmd);
}
@@ -1402,7 +1460,7 @@
netcmd_item_type_to_string(node.type, typestr);
L(F("queueing request for refinement of %s node '%s', level %d\n")
% typestr % hpref % static_cast(node.level));
- netcmd cmd(protocol_version);
+ netcmd cmd;
cmd.write_refine_cmd(node);
write_netcmd_and_try_flush(cmd);
}
@@ -1432,7 +1490,7 @@
L(F("queueing request for data of %s item '%s'\n")
% typestr % hid);
- netcmd cmd(protocol_version);
+ netcmd cmd;
cmd.write_send_data_cmd(type, item);
write_netcmd_and_try_flush(cmd);
note_item_requested(type, item);
@@ -1468,7 +1526,7 @@
L(F("queueing request for contents of %s delta '%s' -> '%s'\n")
% typestr % base_hid % ident_hid);
- netcmd cmd(protocol_version);
+ netcmd cmd;
cmd.write_send_delta_cmd(type, base, ident);
write_netcmd_and_try_flush(cmd);
note_item_requested(type, ident);
@@ -1493,7 +1551,7 @@
L(F("queueing %d bytes of data for %s item '%s'\n")
% dat.size() % typestr % hid);
- netcmd cmd(protocol_version);
+ netcmd cmd;
cmd.write_data_cmd(type, item, dat);
write_netcmd_and_try_flush(cmd);
note_item_sent(type, item);
@@ -1523,7 +1581,7 @@
L(F("queueing %s delta '%s' -> '%s'\n")
% typestr % base_hid % ident_hid);
- netcmd cmd(protocol_version);
+ netcmd cmd;
cmd.write_delta_cmd(type, base, ident, del);
write_netcmd_and_try_flush(cmd);
note_item_sent(type, ident);
@@ -1546,7 +1604,7 @@
L(F("queueing note of nonexistance of %s item '%s'\n")
% typestr % hid);
- netcmd cmd(protocol_version);
+ netcmd cmd;
cmd.write_nonexistant_cmd(type, item);
write_netcmd_and_try_flush(cmd);
}
@@ -1632,22 +1690,6 @@
W(F("No branches found."));
}
-void
-convert_pattern(utf8 & pat, utf8 & conv)
-{
- string x = pat();
- string pattern = "";
- string e = ".|*?+()[]{}^$\\";
- for (string::const_iterator i = x.begin(); i != x.end(); i++)
- {
- if (e.find(*i) != e.npos)
- pattern += '\\';
- pattern += *i;
- }
- conv = pattern + ".*";
-}
-
-
static const var_domain known_servers_domain = var_domain("known-servers");
bool
@@ -1712,13 +1754,6 @@
}
utf8 pat(pattern);
- if (protocol_version < 5)
- {
- W(F("Talking to an old server. "
- "Using %s as a collection, not a regex.") % pattern);
- convert_pattern(pattern, pat);
- this->pattern_re = boost::regex(pat());
- }
vector branchnames;
set ok_branches;
get_branches(app, branchnames);
@@ -1752,11 +1787,12 @@
// make a new nonce of our own and send off the 'auth'
queue_auth_cmd(this->role, this->pattern(), our_key_hash_raw,
- nonce, mk_nonce(), sig_raw());
+ nonce, mk_nonce(), sig_raw(), their_key_encoded);
}
else
{
- queue_anonymous_cmd(this->role, this->pattern(), mk_nonce());
+ queue_anonymous_cmd(this->role, this->pattern(),
+ mk_nonce(), their_key_encoded);
}
return true;
}
@@ -1774,18 +1810,8 @@
bool
session::process_anonymous_cmd(protocol_role role,
- string const & pattern,
- id const & nonce2)
+ string const & pattern)
{
- hexenc hnonce2;
- encode_hexenc(nonce2, hnonce2);
-
- L(F("received 'anonymous' netcmd from client for pattern '%s' "
- "in %s mode with nonce2 '%s'\n")
- % pattern % (role == source_and_sink_role ? "source and sink" :
- (role == source_role ? "source " : "sink"))
- % hnonce2);
-
//
// internally netsync thinks in terms of sources and sinks. users like
// thinking of repositories as "readonly", "readwrite", or "writeonly".
@@ -1846,15 +1872,6 @@
rebuild_merkle_trees(app, ok_branches);
- // get our private key and sign back
- L(F("anonymous read permitted, signing back nonce\n"));
- base64 sig;
- rsa_sha1_signature sig_raw;
- base64< arc4 > our_priv;
- load_priv_key(app, app.signing_key, our_priv);
- make_signature(app.lua, app.signing_key, our_priv, nonce2(), sig);
- decode_base64(sig, sig_raw);
- queue_confirm_cmd(sig_raw());
this->pattern = pattern;
this->remote_peer_key_name = rsa_keypair_id("");
this->authenticated = true;
@@ -1862,20 +1879,16 @@
return true;
}
-bool
-session::process_auth_cmd(protocol_role role,
- string const & pattern,
- id const & client,
- id const & nonce1,
- id const & nonce2,
+bool
+session::process_auth_cmd(protocol_role role,
+ string const & pattern,
+ id const & client,
+ id const & nonce1,
string const & signature)
{
I(this->remote_peer_key_hash().size() == 0);
I(this->saved_nonce().size() == constants::merkle_hash_length_in_bytes);
- hexenc hnonce1, hnonce2;
- encode_hexenc(nonce1, hnonce1);
- encode_hexenc(nonce2, hnonce2);
hexenc their_key_hash;
encode_hexenc(client, their_key_hash);
set ok_branches;
@@ -1889,12 +1902,6 @@
}
boost::regex reg(pattern);
- L(F("received 'auth' netcmd from client '%s' for pattern '%s' "
- "in %s mode with nonce1 '%s' and nonce2 '%s'\n")
- % their_key_hash % pattern % (role == source_and_sink_role ? "source and sink" :
- (role == source_role ? "source " : "sink"))
- % hnonce1 % hnonce2);
-
// check that they replied with the nonce we asked for
if (!(nonce1 == this->saved_nonce))
{
@@ -2000,13 +2007,6 @@
{
// get our private key and sign back
L(F("client signature OK, accepting authentication\n"));
- base64 sig;
- rsa_sha1_signature sig_raw;
- base64< arc4 > our_priv;
- load_priv_key(app, app.signing_key, our_priv);
- make_signature(app.lua, app.signing_key, our_priv, nonce2(), sig);
- decode_base64(sig, sig_raw);
- queue_confirm_cmd(sig_raw());
this->pattern = pattern;
this->pattern_re = boost::regex(this->pattern());
this->authenticated = true;
@@ -2035,6 +2035,18 @@
return false;
}
+void
+session::respond_to_auth_cmd(rsa_oaep_sha_data hmac_key_encrypted)
+{
+ L(F("Writing HMAC confirm command"));
+ base64< arc4 > our_priv;
+ load_priv_key(app, app.signing_key, our_priv);
+ string hmac_key;
+ decrypt_rsa(app.lua, app.signing_key, our_priv, hmac_key_encrypted, hmac_key);
+ session_key = netsync_session_key(hmac_key);
+ queue_confirm_cmd();
+}
+
bool
session::process_confirm_cmd(string const & signature)
{
@@ -2061,20 +2073,6 @@
if (check_signature(app.lua, their_id, their_key, this->saved_nonce(), sig))
{
L(F("server signature OK, accepting authentication\n"));
- this->authenticated = true;
-
- merkle_ptr root;
- load_merkle_node(epoch_item, 0, get_root_prefix().val, root);
- queue_refine_cmd(*root);
- queue_done_cmd(0, epoch_item);
-
- load_merkle_node(key_item, 0, get_root_prefix().val, root);
- queue_refine_cmd(*root);
- queue_done_cmd(0, key_item);
-
- load_merkle_node(cert_item, 0, get_root_prefix().val, root);
- queue_refine_cmd(*root);
- queue_done_cmd(0, cert_item);
return true;
}
else
@@ -2089,6 +2087,23 @@
return false;
}
+void
+session::respond_to_confirm_cmd()
+{
+ merkle_ptr root;
+ load_merkle_node(epoch_item, 0, get_root_prefix().val, root);
+ queue_refine_cmd(*root);
+ queue_done_cmd(0, epoch_item);
+
+ load_merkle_node(key_item, 0, get_root_prefix().val, root);
+ queue_refine_cmd(*root);
+ queue_done_cmd(0, key_item);
+
+ load_merkle_node(cert_item, 0, get_root_prefix().val, root);
+ queue_refine_cmd(*root);
+ queue_done_cmd(0, cert_item);
+}
+
static bool
data_exists(netcmd_item_type type,
id const & item,
@@ -2981,8 +2996,6 @@
rsa_pub_key server_key;
id nonce;
cmd.read_hello_cmd(server_keyname, server_key, nonce);
- if (cmd.get_version() < protocol_version)
- protocol_version = cmd.get_version();
return process_hello_cmd(server_keyname, server_key, nonce);
}
break;
@@ -2996,11 +3009,17 @@
{
protocol_role role;
string pattern;
- id nonce2;
- cmd.read_anonymous_cmd(role, pattern, nonce2);
- if (cmd.get_version() < protocol_version)
- protocol_version = cmd.get_version();
- return process_anonymous_cmd(role, pattern, nonce2);
+ rsa_oaep_sha_data hmac_key_encrypted;
+ cmd.read_anonymous_cmd(role, pattern, hmac_key_encrypted);
+ L(F("received 'anonymous' netcmd from client for pattern '%s' "
+ "in %s mode\n")
+ % pattern % (role == source_and_sink_role ? "source and sink" :
+ (role == source_role ? "source " : "sink")));
+
+ if (!process_anonymous_cmd(role, pattern))
+ return false;
+ respond_to_auth_cmd(hmac_key_encrypted);
+ return true;
}
break;
@@ -3011,11 +3030,25 @@
protocol_role role;
string pattern, signature;
id client, nonce1, nonce2;
- cmd.read_auth_cmd(role, pattern, client, nonce1, nonce2, signature);
- if (cmd.get_version() < protocol_version)
- protocol_version = cmd.get_version();
- return process_auth_cmd(role, pattern, client,
- nonce1, nonce2, signature);
+ rsa_oaep_sha_data hmac_key_encrypted;
+ cmd.read_auth_cmd(role, pattern, client, nonce1,
+ hmac_key_encrypted, signature);
+
+ hexenc their_key_hash;
+ encode_hexenc(client, their_key_hash);
+ hexenc hnonce1;
+ encode_hexenc(nonce1, hnonce1);
+
+ L(F("received 'auth(hmac)' netcmd from client '%s' for pattern '%s' "
+ "in %s mode with nonce1 '%s'\n")
+ % their_key_hash % pattern % (role == source_and_sink_role ? "source and sink" :
+ (role == source_role ? "source " : "sink"))
+ % hnonce1);
+
+ if (!process_auth_cmd(role, pattern, client, nonce1, signature))
+ return false;
+ respond_to_auth_cmd(hmac_key_encrypted);
+ return true;
}
break;
@@ -3024,8 +3057,10 @@
require(voice == client_voice, "confirm netcmd received in client voice");
{
string signature;
- cmd.read_confirm_cmd(signature);
- return process_confirm_cmd(signature);
+ cmd.read_confirm_cmd();
+ this->authenticated = true;
+ respond_to_confirm_cmd();
+ return true;
}
break;
@@ -3147,7 +3182,7 @@
{
if (!armed)
{
- if (cmd.read(inbuf))
+ if (cmd.read(inbuf, session_key, read_hmac))
{
// inbuf.erase(0, cmd.encoded_size());
armed = true;
@@ -3175,7 +3210,7 @@
}
catch (bad_decode & bd)
{
- W(F("caught bad_decode exception processing peer %s: '%s'\n") % peer_id % bd.what);
+ W(F("protocol error while processing peer %s: '%s'\n") % peer_id % bd.what);
return false;
}
}
@@ -3208,7 +3243,7 @@
}
catch (bad_decode & bd)
{
- W(F("caught bad_decode exception decoding input from peer %s: '%s'\n")
+ W(F("protocol error while processing peer %s: '%s'\n")
% sess.peer_id % bd.what);
return;
}
@@ -3235,7 +3270,7 @@
}
catch (bad_decode & bd)
{
- W(F("caught bad_decode exception decoding input from peer %s: '%s'\n")
+ W(F("protocol error while processing peer %s: '%s'\n")
% sess.peer_id % bd.what);
return;
}
@@ -3309,7 +3344,7 @@
}
catch (bad_decode & bd)
{
- W(F("caught bad_decode exception decoding input from peer %s: '%s', marking as bad\n")
+ W(F("protocol error while processing peer %s: '%s', marking as bad\n")
% i->second->peer_id % bd.what);
arm_failed.insert(i->first);
}
@@ -3366,7 +3401,7 @@
}
catch (bad_decode & bd)
{
- W(F("caught bad_decode exception decoding input from peer %s: '%s', disconnecting\n")
+ W(F("protocol error while processing peer %s: '%s', disconnecting\n")
% sess->peer_id % bd.what);
sessions.erase(fd);
live_p = false;
--- vocab.cc
+++ vocab.cc
@@ -102,7 +102,43 @@
val.ok = true;
}
+inline void
+verify(netsync_session_key & val)
+{
+ if (val.ok)
+ return;
+ if (val().size() == 0)
+ {
+ val.s.append(constants::netsync_session_key_length_in_bytes, 0);
+ return;
+ }
+
+ N(val().size() == constants::netsync_session_key_length_in_bytes,
+ F("Invalid key length of %d bytes") % val().length());
+
+ val.ok = true;
+}
+
+inline void
+verify(netsync_hmac_value & val)
+{
+ if (val.ok)
+ return;
+
+ if (val().size() == 0)
+ {
+ val.s.append(constants::netsync_hmac_value_length_in_bytes, 0);
+ return;
+ }
+
+ N(val().size() == constants::netsync_hmac_value_length_in_bytes,
+ F("Invalid key length of %d bytes") % val().length());
+
+ val.ok = true;
+}
+
+
inline void
verify(local_path & val)
{
--- vocab_terms.hh
+++ vocab_terms.hh
@@ -34,7 +34,11 @@
ATOMIC_NOVERIFY(rsa_pub_key); // some nice numbers
ATOMIC_NOVERIFY(rsa_priv_key); // some nice numbers
ATOMIC_NOVERIFY(rsa_sha1_signature); // some other nice numbers
+ATOMIC_NOVERIFY(rsa_oaep_sha_data);
+ATOMIC(netsync_session_key); // key for netsync session HMAC
+ATOMIC(netsync_hmac_value); // 160-bit SHA-1 HMAC
+
DECORATE(revision); // thing associated with a revision
DECORATE(manifest); // thing associated with a manifest
DECORATE(file); // thing associated with a file