Filename: 262-rekey-circuits.txt
Title: Re-keying live circuits with new cryptographic material
Author: Nick Mathewson
Created: 28 Dec 2015
Status: Reserve

   [NOTE: This proposal is in "Reserve" status because the issue it
   addresses should be solved by any future relay encryption
   protocol. (2020 July 31)]

1. Summary and Motivation

   Cryptographic primitives have an upper limit of how much data should
   be encrypted with the same key.  But currently Tor circuits have no
   upper limit of how much data they will deliver.

   While the upper limits of our AES-CTR crypto is ridiculously high
   (on the order of hundreds of exabytes), the AEZ crypto we're
   considering suggests we should rekey after the equivalent in cells
   after around 280 TB.  280 TB is still high, but not ridiculously
   high.

   So in this proposal I explain a general mechanism for rekeying a
   circuit.  We shouldn't actually build this unless we settle on

2. RELAY_REKEY cell operation

   To rekey, the circuit initiator ("client") can send a new RELAY_REKEY cell
   type:

        struct relay_rekey {
          u16 rekey_method IN [0, 1];
          u8 rekey_data[];
        }

        const REKEY_METHOD_ACK = 0;
        const REKEY_METHOD_SHAKE256_CLIENT = 1;

   This cell means "I am changing the key." The new key material will be
   derived from SHAKE256 of the aez_key concatenated with the rekey_data
   field, to fill a new shake_output structure.  The client should set
   rekey_data at random.

   After sending one of these RELAY_REKEY cells, the client uses the new
   aez_key to encrypt all of its data to this hop, but retains the old
   aez_key for decrypting the data coming back from the relay.

   When the relay receives a RELAY_REKEY cell, it sends a RELAY_REKEY
   cell back towards the client, with empty rekey_data, and
   relay_method==0, and then updates its own key material for all
   additional data it sends and receives to the client.

   When the client receives this reply, it can discard the old AEZ key, and
   begin decrypting subsequent inbound cells with the new key.


   So in summary: the client sends a series of cells encrypted with the
   old key, and then sends a REKEY cell, followed by relay cells
   encrypted with the new key:

     OldKey[data data data ... data rekey] NewKey[data data data...]

   And after the server receives the REKEY cell, it stops sending relay
   cells encrypted with the old keys, sends its own REKEY cell with the
   ACK method, and starts sending cells encrypted with the new key.

       REKEY arrives somewhere in here
                   I
                   V
     OldKey[data data data data rekey-ack] NewKey[data data data ...]

2.1. Supporting other cryptography types

   Each relay cipher must specify its own behavior in the presence of a
   REKEY cell of each type that it supports.  In general, the behavior
   of method 1 ("shake256-client") is "regenerate keys as if we were
   calling the original KDF after a CREATE handshake, using SHAKE256 on
   our current static key material and on a 32-byte random input."

   The behavior of any unsupported REKEY method must be to close the
   circuit with an error.

   The AES-CTR relay cell crypto doesn't support rekeying. See 3.2 below
   if you disagree.

2.2. How often to re-key?

   Clients should follow a deterministic algorithm in deciding when to
   re-key, so as not to leak client differences.  This algorithm should
   be type-specific.  For AEZ, I recommend that clients conservatively
   rekey every 2**32 cells (about 2 TB).  And to make sure that this
   code actually works, the schedule should be after 2**15 cells, and
   then every 2**32 cells thereafter.

   It may be beneficial to randomize these numbers.  If so, let's try
   subtracting between 0 and 25% at random.

2.3. How often to allow re-keying?

   We could define a lower bound to prevent too-frequent rekeying.  I'm
   not sure I see the point here; the process described above is not
   that costly.

3.  Alternative designs

3.1. Should we add some public key cryptography here?

   We could change the body of a REKEY cell and its ack to be more like
   CREATE/CREATED.  Then we'd have to add a third step from the client
   to the server to acknowledge receipt of the 'CREATED' cell and
   changing of the key.

   So, what would this added complexity and computational load buy us?
   It would defend against the case where an adversary had compromised
   the shared key material for a circuit, but was not able to compromise
   the rekey process.  I'm not sure that this is reasonable; the
   likeliest cases I can think of here seem to be "get compromised, stay
   compromised" for a circuit.

3.2. Hey, could we use this for forward secrecy with AES-CTR?

   We could, but the best solution to AES-CTR's limitations right now is
   to stop using our AES-CTR setup.  Anything that supports REKEY will
   also presumably support AEZ or something better.

3.3. We could upgrade ciphers with this!

   Yes we could.  We could define this not only to change the key, but
   to upgrade to a better ciphersuite.  For example, we could start by
   negotiating AES-CTR, and then "secretly" upgrade to AEZ.  I'm not
   sure that's worth the complexity, or that it would really be secret
   in the presence of traffic analysis.