# # patch "constants.cc" # from [eabc0d5838a67e3e4d351ce3d28eac0387f773ae] # to [18838196cb64a85337deb2184c7e42c865133868] # # patch "constants.hh" # from [4a4494bcaa97ec07c147fc066564519bb4d13481] # to [165716b9b2fee13374d0e18d3c4daf940475eb9e] # # patch "netcmd.cc" # from [e04a41fed951a77dcef389e94cc26d04ae12220f] # to [4025dbca594a9c4da3c2bde9257de1f592e1d6b9] # # patch "netcmd.hh" # from [5626696805eab24ff5bd23f114a749b3aa78645a] # to [cade7a00aa72deb9ad34b9047f26f04c4dbeecaa] # # patch "netsync.cc" # from [46cef8d28ddd981f6ca9c8483434fa59efad0f0d] # to [10aeefac2f35cdf860b60b2301c51d6da490c455] # # patch "vocab.cc" # from [6e07baffa263c80f013d474ad362c3b52c162201] # to [b63535f4a650ae9ec89080324f4c8eac4fdf5402] # # patch "vocab_terms.hh" # from [37273b9af033700b030b6831c12a3ad6aacd27c7] # to [cf97b245c5684ff606ffbd6fff05032c2893de24] # --- constants.cc +++ constants.cc @@ -160,5 +160,6 @@ 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 } --- constants.hh +++ constants.hh @@ -120,6 +120,9 @@ // 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; + } #endif // __CONSTANTS_HH__ --- 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" @@ -69,13 +71,27 @@ } void -netcmd::write(string & out) const +netcmd::write(string & out, netsync_session_key const & key) const { 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); + if (version < 5) + { + adler32 check(reinterpret_cast(payload.data()), payload.size()); + insert_datum_lsb(check.sum(), out); + } + else + { + CryptoPP::HMAC hmac(reinterpret_cast(key().data()), + key().length()); + char digest[CryptoPP::SHA::DIGESTSIZE]; + hmac.CalculateDigest(reinterpret_cast(digest), + reinterpret_cast(payload.data()), + payload.size()); + out.append(digest, sizeof(digest)); + } } // last should be zero (doesn't mean we're compatible with version 0). @@ -95,7 +111,7 @@ } bool -netcmd::read(string & inbuf) +netcmd::read(string & inbuf, netsync_session_key const & key) { size_t pos = 0; @@ -156,11 +172,22 @@ 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 (version < 5) { - inbuf.reserve(pos + payload_len + sizeof(u32) + constants::bufsz); - return false; + if (inbuf.size() < pos + payload_len + sizeof(u32)) + { + inbuf.reserve(pos + payload_len + sizeof(u32) + constants::bufsz); + return false; + } } + else + { + if (inbuf.size() < pos + payload_len + CryptoPP::SHA::DIGESTSIZE) + { + 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. @@ -172,12 +199,31 @@ pos = 0; // they might have given us bogus data - u32 checksum = extract_datum_lsb(inbuf, pos, "netcmd checksum"); - 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()); + if (version < 5) + { + u32 checksum = extract_datum_lsb(inbuf, pos, "netcmd checksum"); + 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()); + } + else + { + string cmd_digest = extract_substring(inbuf, pos, CryptoPP::SHA::DIGESTSIZE, + "netcmd HMAC"); + inbuf.erase(0, pos); + char digest_buf[CryptoPP::SHA::DIGESTSIZE]; + CryptoPP::HMAC hmac(reinterpret_cast(key().data()), + key().length()); + 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)); + } return true; } --- netcmd.hh +++ netcmd.hh @@ -62,8 +62,8 @@ // 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_session_key()) const; + bool read(std::string & inbuf, netsync_session_key const & key = netsync_session_key()); // i/o functions for each type of command payload void read_error_cmd(std::string & errmsg) const; --- netsync.cc +++ netsync.cc @@ -228,6 +228,7 @@ utf8 pattern; id remote_peer_key_hash; rsa_keypair_id remote_peer_key_name; + netsync_session_key session_key; bool authenticated; time_t last_io_time; @@ -726,7 +727,7 @@ session::write_netcmd_and_try_flush(netcmd const & cmd) { if (!encountered_error) - cmd.write(outbuf); + cmd.write(outbuf, session_key); 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 @@ -3145,7 +3146,7 @@ { if (!armed) { - if (cmd.read(inbuf)) + if (cmd.read(inbuf, session_key)) { // inbuf.erase(0, cmd.encoded_size()); armed = true; --- vocab.cc +++ vocab.cc @@ -102,7 +102,24 @@ val.ok = true; } +inline void +verify(netsync_session_key & val) +{ + if (val.ok) + return; + if (val().length() == 0) + { + val.s.append(constants::netsync_session_key_length_in_bytes, 0); + return; + } + + N(val().length() == constants::netsync_session_key_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 @@ -36,6 +36,8 @@ ATOMIC_NOVERIFY(rsa_sha1_signature); // some other nice numbers ATOMIC_NOVERIFY(rsa_oaep_sha_data); +ATOMIC(netsync_session_key); // key for netsync session HMAC + DECORATE(revision); // thing associated with a revision DECORATE(manifest); // thing associated with a manifest DECORATE(file); // thing associated with a file