# # patch "ChangeLog" # from [f6962ae1e54db8ef7df9d8daf40c08415d3b60d7] # to [a7d78e5b4cb7e07bdf3aba7a92ea4fc609a03316] # # patch "Makefile.am" # from [8f57b338067b43d1d1ae5de118323a6c67f6be7d] # to [f2cebcb5b9a6cf9ca254765464af8cccc11da225] # # patch "netcmd.cc" # from [7308c12cecebbd725ddca9ebfa94bd9a6e1efdea] # to [f1f30a4a9655a95d2007a309a0d4a618229dcdca] # # patch "netcmd.hh" # from [db259c3bbd1fdc984b3cae3fb90128f7b0e461e5] # to [f5565d11c6b2db20139d0aab07a5b8bfe37b3cd8] # # patch "netsync.cc" # from [a216c4dc84981cff4cdf26cc45303c46a16054fd] # to [c0daeb05b828a4e64c62379ceb8f2113c3f8b1ae] # # patch "transforms.hh" # from [beb9c11a04e735f442928aea2e984834c8573c8d] # to [9fb4f54ab766397cc5a40885457b2ddbabbb300f] # --- ChangeLog +++ ChangeLog @@ -1,3 +1,15 @@ +2005-06-26 Matt Johnston + + * netcmd.cc (netcmd::read, netcmd::write): change to using a HMACs + chained by including the previous HMAC in the input data, rather + than altering the key each time. + * netcmd.cc ({read,write}_{data,delta}_cmd): use encode_gzip/decode_gzip + rather than raw xform. + * hmac.{cc,hh}: new chained_hmac abstraction + * Makefile.in: add them + * netsync.cc: each session keeps a chained_hmac for read/write + * transforms.hh: add a string variant for encode_gzip + 2005-06-25 Nathaniel Smith * netsync.cc: Tweak comment. --- Makefile.am +++ Makefile.am @@ -40,6 +40,7 @@ selectors.cc selectors.hh \ annotate.cc annotate.hh \ restrictions.cc restrictions.hh \ + hmac.cc hmac.hh \ \ cleanup.hh unit_tests.hh interner.hh \ cycle_detector.hh randomfile.hh adler32.hh quick_alloc.hh \ --- netcmd.cc +++ netcmd.cc @@ -7,10 +7,6 @@ #include #include -#include "cryptopp/gzip.h" -#include "cryptopp/hmac.h" -#include "cryptopp/sha.h" - #include "adler32.hh" #include "constants.hh" #include "netcmd.hh" @@ -18,6 +14,7 @@ #include "numeric_vocab.hh" #include "sanity.hh" #include "transforms.hh" +#include "hmac.hh" using namespace std; using namespace boost; @@ -68,34 +65,19 @@ } void -netcmd::write(string & out, netsync_session_key const & key, - netsync_hmac_value & hmac_val) const +netcmd::write(string & out, chained_hmac & hmac) const { size_t oldlen = out.size(); out += static_cast(version); out += static_cast(cmd_code); insert_variable_length_string(payload, out); - 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[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); + string digest = hmac.process(out, oldlen); + out.append(digest); } bool -netcmd::read(string & inbuf, netsync_session_key const & key, - netsync_hmac_value & hmac_val) +netcmd::read(string & inbuf, chained_hmac & hmac) { size_t pos = 0; @@ -142,15 +124,19 @@ 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 + CryptoPP::SHA::DIGESTSIZE) + if (inbuf.size() < pos + payload_len + constants::netsync_hmac_value_length_in_bytes) { - inbuf.reserve(pos + payload_len + CryptoPP::SHA::DIGESTSIZE + constants::bufsz); + inbuf.reserve(pos + payload_len + constants::netsync_hmac_value_length_in_bytes + 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"); + + // grab it before the data gets munged + string digest = hmac.process(inbuf, 0, pos + payload_len); + payload = inbuf.substr(pos + payload_len); inbuf.erase(pos + payload_len, inbuf.npos); inbuf.swap(payload); @@ -158,26 +144,13 @@ pos = 0; // they might have given us bogus data - string cmd_digest = extract_substring(inbuf, pos, CryptoPP::SHA::DIGESTSIZE, + string cmd_digest = extract_substring(inbuf, pos, + constants::netsync_hmac_value_length_in_bytes, "netcmd HMAC"); inbuf.erase(0, pos); - 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; @@ -445,7 +418,13 @@ extract_variable_length_string(payload, dat, pos, "data netcmd, data payload"); if (compressed_p == 1) - dat = xform(dat); + { + gzip zdat; + data tdat; + zdat.swap(dat); + decode_gzip(zdat, tdat); + tdat.swap(dat); + } assert_end_of_buffer(payload, pos, "data netcmd payload"); } @@ -460,8 +439,10 @@ payload += item(); if (dat.size() > constants::netcmd_minimum_bytes_to_bother_with_gzip) { + gzip zdat; + encode_gzip(dat, zdat); string tmp; - tmp = xform(dat); + zdat.swap(tmp); payload += static_cast(1); // compressed flag insert_variable_length_string(tmp, payload); } @@ -493,8 +474,14 @@ extract_variable_length_string(payload, tmp, pos, "delta netcmd, delta payload"); if (compressed_p == 1) - tmp = xform(tmp); - del = delta(tmp); + { + gzip zdel(tmp); + decode_gzip(zdel, del); + } + else + { + del = tmp; + } assert_end_of_buffer(payload, pos, "delta netcmd payload"); } @@ -510,16 +497,19 @@ payload += base(); payload += ident(); - string tmp = del(); + string tmp; if (tmp.size() > constants::netcmd_minimum_bytes_to_bother_with_gzip) { payload += static_cast(1); // compressed flag - tmp = xform(tmp); + gzip zdel; + encode_gzip(del, zdel); + tmp = zdel(); } else { payload += static_cast(0); // compressed flag + tmp = del(); } I(tmp.size() <= constants::netcmd_payload_limit); insert_variable_length_string(tmp, payload); --- netcmd.hh +++ netcmd.hh @@ -12,6 +12,7 @@ #include "merkle_tree.hh" #include "numeric_vocab.hh" #include "vocab.hh" +#include "hmac.hh" typedef enum { @@ -63,10 +64,9 @@ // basic cmd i/o (including checksums) void write(std::string & out, - netsync_session_key const & key, - netsync_hmac_value & hmac_val) const; + chained_hmac & hmac) const; bool read(std::string & inbuf, - netsync_session_key const & key, netsync_hmac_value & hmac_val); + chained_hmac & hmac); // i/o functions for each type of command payload void read_error_cmd(std::string & errmsg) const; --- netsync.cc +++ netsync.cc @@ -32,6 +32,7 @@ #include "xdelta.hh" #include "epoch.hh" #include "platform.hh" +#include "hmac.hh" #include "cryptopp/osrng.h" @@ -271,8 +272,8 @@ 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; + chained_hmac read_hmac; + chained_hmac write_hmac; bool authenticated; time_t last_io_time; @@ -321,6 +322,8 @@ id mk_nonce(); void mark_recent_io(); + void set_session_key(string const & key); + void setup_client_tickers(); bool done_all_refinements(); @@ -629,6 +632,14 @@ } void +session::set_session_key(string const & key) +{ + session_key = netsync_session_key(key); + read_hmac.set_key(session_key); + write_hmac.set_key(session_key); +} + +void session::setup_client_tickers() { byte_in_ticker.reset(new ticker("bytes in", ">", 1024, true)); @@ -777,7 +788,7 @@ session::write_netcmd_and_try_flush(netcmd const & cmd) { if (!encountered_error) - cmd.write(outbuf, session_key, write_hmac); + cmd.write(outbuf, 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 @@ -1422,7 +1433,7 @@ 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()); + set_session_key(nonce2()); } void @@ -1440,7 +1451,7 @@ 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()); + set_session_key(nonce2()); } void @@ -2043,7 +2054,7 @@ 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); + set_session_key(hmac_key); queue_confirm_cmd(); } @@ -3182,7 +3193,7 @@ { if (!armed) { - if (cmd.read(inbuf, session_key, read_hmac)) + if (cmd.read(inbuf, read_hmac)) { // inbuf.erase(0, cmd.encoded_size()); armed = true; --- transforms.hh +++ transforms.hh @@ -73,7 +73,12 @@ void decode_gzip(gzip const & in, T & out) { out = xform(in()); } +// string variant for netsync +template +void encode_gzip(std::string const & in, gzip & out) +{ out = xform(in); } +#endif // both at once (this is relatively common) template