Filename: 357-circ-key-exporters.md
Title: Circuit key exporters: A better way to use KH
Author: Nick Mathewson
Created: 27 March 2025
Status: Open

Introduction

Note: This proposal does not call for an immediate change in the Tor protocols. Rather, it describes a convention we should use for per-circuit key exporters in the future.

In the current Tor protocols, we derive a shared secret "KH" as part of our circuit extension handshakes. This "KH" value is used for a single purpose:

As part of the hidden service introduction protocol, to prevent an attacker from replaying an ESTABLISH_INTRO message.

However, its character and usage are ad-hoc. If we want to use it anywhere else, we will risk exposing ourselves to cross-protocol attacks.

Here we specify a new convention for circuit binding. This convention should be used for any new cases where we need a shared secret shared by the client and a given hop on a circuit.

Also, if we ever define a new handshake to use in ESTABLISH_INTRO we should use this convention in place of our current use of KH.

Proposal

As part of our handshake, we should derive a KH value, now called a "circuit binding seed". Future KH values should be 32 bytes long.

To derive an exported secret from KH, we will use cSHAKE128, with these parameters:

  • X (main input string) set to KH
  • N (function name) set to ""
  • S (customization string) as a customization string starting with "Tor:"
  • L (the output length) as desired.

We denote this exported secret as:

  CircExporter(Circuit[Hop], Custom, Len)
    = cSHAKE128(X=Circuit[Hop].KH, L=Len, N="", C=Custom)

For example, in the future when we're computing our MAC for an ESTABLISH_INTRO message, instead of using KH directly as our MAC key, we might use CircExporter(Circuit[Hop], "Tor:ESTABLISH_INTRO MAC", 32) as our MAC key.

Historical note

In the obsolete "TAP" format, the KH field was sent in the clear as part of the CREATED cell. This is not the case in the ntor handshake and beyond.