The unified diff between revisions [869f0c37..] and [e24ccc23..] is displayed below. It can also be downloaded as a raw diff.

This diff has been restricted to the following files: 'netsync.cc'

#
#
# patch "netsync.cc"
#  from [218fabbe4a06f50089a50833c72f9c1b4ed78a91]
#    to [cba71cd069c2658e79568849fe0328c92695547d]
#
============================================================
--- netsync.cc	218fabbe4a06f50089a50833c72f9c1b4ed78a91
+++ netsync.cc	cba71cd069c2658e79568849fe0328c92695547d
@@ -112,18 +112,34 @@
 // 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 (adler32 for pre-v5, HMAC for post-v5) 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 +151,31 @@
 // "hello <id> <nonce>" 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) <pattern> <id> <nonce1> <hmac key>
+// <sig>" 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) <pattern> <hmac key>" 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 <id> <nonce>" 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)
 // <pattern> <id> <nonce1> <nonce2> <sig>" command which identifies its
 // RSA key, notes the role it wishes to play in the synchronization,
@@ -146,6 +187,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 +264,6 @@ session
   string outbuf;

   netcmd cmd;
-  u8 protocol_version;
   bool armed;
   bool arm();

@@ -229,6 +271,9 @@ session
   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 +366,16 @@ session
                        id const & nonce);
   void queue_anonymous_cmd(protocol_role role,
                            string const & pattern,
-                           id const & nonce2);
+                           id const & nonce2,
+                           base64<rsa_pub_key> 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<rsa_pub_key> 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 +399,15 @@ session
                          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 +480,14 @@ session::session(protocol_role role,
   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 +778,7 @@ session::write_netcmd_and_try_flush(netc
 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
@@ -1320,7 +1369,7 @@ session::queue_bye_cmd()
 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;
@@ -1330,7 +1379,7 @@ session::queue_error_cmd(string const &
 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;
@@ -1343,7 +1392,7 @@ session::queue_done_cmd(size_t level,
   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);
 }
@@ -1352,7 +1401,7 @@ session::queue_hello_cmd(id const & serv
 session::queue_hello_cmd(id const & server,
                          id const & nonce)
 {
-  netcmd cmd(protocol_version);
+  netcmd cmd;
   hexenc<id> server_encoded;
   encode_hexenc(server, server_encoded);

@@ -1369,11 +1418,16 @@ session::queue_anonymous_cmd(protocol_ro
 void
 session::queue_anonymous_cmd(protocol_role role,
                              string const & pattern,
-                             id const & nonce2)
+                             id const & nonce2,
+                             base64<rsa_pub_key> 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
@@ -1382,18 +1436,23 @@ session::queue_auth_cmd(protocol_role ro
                         id const & client,
                         id const & nonce1,
                         id const & nonce2,
-                        string const & signature)
+                        string const & signature,
+                        base64<rsa_pub_key> 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);
 }

@@ -1406,7 +1465,7 @@ session::queue_refine_cmd(merkle_node co
   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<int>(node.level));
-  netcmd cmd(protocol_version);
+  netcmd cmd;
   cmd.write_refine_cmd(node);
   write_netcmd_and_try_flush(cmd);
 }
@@ -1436,7 +1495,7 @@ session::queue_send_data_cmd(netcmd_item

   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);
@@ -1472,7 +1531,7 @@ session::queue_send_delta_cmd(netcmd_ite

   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);
@@ -1497,7 +1556,7 @@ session::queue_data_cmd(netcmd_item_type

   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);
@@ -1527,7 +1586,7 @@ session::queue_delta_cmd(netcmd_item_typ

   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);
@@ -1550,7 +1609,7 @@ session::queue_nonexistant_cmd(netcmd_it

   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);
 }
@@ -1636,22 +1695,6 @@ get_branches(app_state & app, vector<str
     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
@@ -1716,13 +1759,6 @@ session::process_hello_cmd(rsa_keypair_i
   }

   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<string> branchnames;
   set<utf8> ok_branches;
   get_branches(app, branchnames);
@@ -1756,11 +1792,12 @@ session::process_hello_cmd(rsa_keypair_i

       // 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;
 }
@@ -1778,18 +1815,8 @@ session::process_anonymous_cmd(protocol_

 bool
 session::process_anonymous_cmd(protocol_role role,
-                               string const & pattern,
-                               id const & nonce2)
+                               string const & pattern)
 {
-  hexenc<id> 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".
@@ -1850,15 +1877,6 @@ session::process_anonymous_cmd(protocol_

   rebuild_merkle_trees(app, ok_branches);

-  // get our private key and sign back
-  L(F("anonymous read permitted, signing back nonce\n"));
-  base64<rsa_sha1_signature> sig;
-  rsa_sha1_signature sig_raw;
-  base64< arc4<rsa_priv_key> > 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;
@@ -1866,20 +1884,16 @@ session::process_anonymous_cmd(protocol_
   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<id> hnonce1, hnonce2;
-  encode_hexenc(nonce1, hnonce1);
-  encode_hexenc(nonce2, hnonce2);
   hexenc<id> their_key_hash;
   encode_hexenc(client, their_key_hash);
   set<utf8> ok_branches;
@@ -1893,12 +1907,6 @@ session::process_auth_cmd(protocol_role
     }
   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))
     {
@@ -2004,13 +2012,6 @@ session::process_auth_cmd(protocol_role
     {
       // get our private key and sign back
       L(F("client signature OK, accepting authentication\n"));
-      base64<rsa_sha1_signature> sig;
-      rsa_sha1_signature sig_raw;
-      base64< arc4<rsa_priv_key> > 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;
@@ -2039,6 +2040,18 @@ session::process_auth_cmd(protocol_role
   return false;
 }

+void
+session::respond_to_auth_cmd(rsa_oaep_sha_data hmac_key_encrypted)
+{
+  L(F("Writing HMAC confirm command"));
+  base64< arc4<rsa_priv_key> > 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)
 {
@@ -2065,20 +2078,6 @@ session::process_confirm_cmd(string cons
       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
@@ -2093,6 +2092,23 @@ session::process_confirm_cmd(string cons
   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,
@@ -2985,8 +3001,6 @@ session::dispatch_payload(netcmd const &
         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;
@@ -3000,11 +3014,17 @@ session::dispatch_payload(netcmd const &
       {
         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;

@@ -3015,11 +3035,25 @@ session::dispatch_payload(netcmd const &
         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<id> their_key_hash;
+        encode_hexenc(client, their_key_hash);
+        hexenc<id> 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;

@@ -3028,8 +3062,10 @@ session::dispatch_payload(netcmd const &
       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;

@@ -3151,7 +3187,7 @@ session::arm()
 {
   if (!armed)
     {
-      if (cmd.read(inbuf))
+      if (cmd.read(inbuf, session_key, read_hmac))
         {
 //          inbuf.erase(0, cmd.encoded_size());
           armed = true;
@@ -3179,7 +3215,7 @@ bool session::process()
     }
   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;
     }
 }
@@ -3212,7 +3248,7 @@ call_server(protocol_role role,
         }
       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;
         }
@@ -3239,7 +3275,7 @@ call_server(protocol_role role,
                 }
               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;
                 }
@@ -3313,7 +3349,7 @@ arm_sessions_and_calculate_probe(Netxx::
         }
       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);
         }
@@ -3370,7 +3406,7 @@ handle_read_available(Netxx::socket_type
         }
       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;
@@ -3616,21 +3652,6 @@ session::rebuild_merkle_trees(app_state
   ticker certs_ticker("certs", "c", 256);
   ticker keys_ticker("keys", "k", 1);

-  // this code is wrong.  the way the logic _should_ work is:
-  //   -- start with all branches we want to include
-  //   -- for each such branch, find all branch certs for that branch
-  //   -- for each such cert, note down its revision
-  //      (or these two steps can be replaced with anything else that gives us
-  //      list of all revisions in the branch)
-  //   -- expand this set of revisions to include all of their ancestors
-  //   -- for each such revision, insert all of its certs into the cert table,
-  //      and note all of its branches and keys
-  //   -- for each such branch, insert its epoch into the epoch table, and for
-  //      each such key, insert its key into the key table.
-  // this somewhat convoluted approach is necessary to handle cases where
-  // ancestors leave the branch inclusion set, where revisions carry branches
-  // that are otherwise outside of the inclusion set, etc.
-
   set<revision_id> revision_ids;
   set<rsa_keypair_id> inserted_keys;

@@ -3649,6 +3670,9 @@ session::rebuild_merkle_trees(app_state
           }
       }

+    // FIXME: we should probably include epochs for all branches mentioned in
+    // any included branch cert, rather than just for branches included by the
+    // branch mask
     {
       map<cert_value, epoch_data> epochs;
       app.db.get_epochs(epochs);