# # 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