aboutsummaryrefslogtreecommitdiff
path: root/rend-spec-v3.txt
diff options
context:
space:
mode:
Diffstat (limited to 'rend-spec-v3.txt')
-rw-r--r--rend-spec-v3.txt114
1 files changed, 88 insertions, 26 deletions
diff --git a/rend-spec-v3.txt b/rend-spec-v3.txt
index 0914c81..a8ac264 100644
--- a/rend-spec-v3.txt
+++ b/rend-spec-v3.txt
@@ -495,12 +495,21 @@ Table of contents:
hidden service descriptors are not signed with the services' public
keys directly. Instead, we use a key-blinding system [KEYBLIND] to
create a new key-of-the-day for each hidden service. Any client that
- knows the hidden service's credential can derive these blinded
+ knows the hidden service's public identity key can derive these blinded
signing keys for a given period. It should be impossible to derive
- the blinded signing key lacking that credential.
+ the blinded signing key lacking that knowledge.
+
+ This is achieved using two nonces:
+
+ * A "credential", derived from the public identity key KP_hs_id.
+ N_hs_cred.
+
+ * A "subcredential", derived from the credential N_hs_cred
+ and information which various with the current time period.
+ N_hs_subcred.
The body of each descriptor is also encrypted with a key derived from
- the credential.
+ the public signing key.
To avoid a "thundering herd" problem where every service generates
and uploads a new descriptor at the start of each period, each
@@ -574,6 +583,7 @@ Table of contents:
to generate blinded signing keys as described in [KEYBLIND]
and [SUBCRED]. The public key is encoded in the ".onion"
address according to [NAMING].
+ KP_hs_id, KS_hs_id.
Blinded signing key -- A keypair derived from the identity key,
used to sign descriptor signing keys. It changes periodically for
@@ -582,6 +592,7 @@ Table of contents:
the public blinded identity key for a service. This key is used
as an index in the DHT-like structure of the directory system
(see [SUBCRED]).
+ KP_blind_id, KS_blind_id.
Descriptor signing key -- A key used to sign hidden service
descriptors. This is signed by blinded signing keys. Unlike
@@ -589,6 +600,7 @@ Table of contents:
of this key must be stored online by hidden service hosts. The
public part of this key is included in the unencrypted section
of HS descriptors (see [DESC-OUTER]).
+ KP_hs_desc_sign, KS_hs_desc_sign.
Introduction point authentication key -- A short-term signing
keypair used to identify a hidden service to a given
@@ -599,23 +611,27 @@ Table of contents:
can get their introduction requests sent to the right
service. No keypair is ever used with more than one introduction
point. (previously called a "service key" in rend-spec.txt)
+ KP_hs_intro_tid, KS_hs_intro_tid
+ ("hidden service introduction point temporary id").
Introduction point encryption key -- A short-term encryption
keypair used when establishing connections via an introduction
point. Plays a role analogous to Tor nodes' onion keys. A fresh
keypair is made for each introduction point.
+ KP_hs_intro_ntor, KS_hs_intro_ntor.
Symmetric keys defined in this document:
Descriptor encryption keys -- A symmetric encryption key used to
encrypt the body of hidden service descriptors. Derived from the
current period and the hidden service credential.
+ K_desc_enc.
Public/private keypairs defined elsewhere:
- Onion key -- Short-term encryption keypair
+ Onion key -- Short-term encryption keypair (KS_onion_ntor, KP_onion_ntor).
- (Node) identity key
+ (Node) identity key (KP_relayid).
Symmetric key-like things defined elsewhere:
@@ -636,11 +652,13 @@ Table of contents:
- An x25519 keypair used to compute decryption keys that allow the client to
decrypt the hidden service descriptor. See [HS-DESC-ENC].
+ KP_hsc_desc_enc, KS_hsd_desc_enc.
- An ed25519 keypair which allows the client to compute signatures which
prove to the hidden service that the client is authorized. These
signatures are inserted into the INTRODUCE1 cell, and without them the
introduction to the hidden service cannot be completed. See [INTRO-AUTH].
+ KP_hsc_intro_auth, KS_hsc_intro_auth.
The right way to exchange these keys is to have the client generate keys and
send the corresponding public keys to the hidden service out-of-band. An
@@ -681,11 +699,11 @@ Table of contents:
The subcredential for a period is derived as:
- subcredential = H("subcredential" | credential | blinded-public-key).
+ N_hs_subcred = H("subcredential" | N_hs_cred | blinded-public-key).
In the above formula, credential corresponds to:
- credential = H("credential" | public-identity-key)
+ N_hs_cred = H("credential" | public-identity-key)
where public-identity-key is the public identity master key of the hidden
service.
@@ -704,6 +722,7 @@ Table of contents:
* the daily subcredential,
* the hidden service directories' public keys,
* a shared random value that changes in each time period,
+ shared_random_value.
* a set of network-wide networkstatus consensus parameters.
(Consensus parameters are integer values voted on by authorities
and published in the consensus documents, described in
@@ -724,10 +743,11 @@ Table of contents:
Time periods start at the Unix epoch (Jan 1, 1970), and are computed by
taking the number of minutes since the epoch and dividing by the time
- period. However, we want our time periods to start at 12:00UTC every day, so
- we subtract a "rotation time offset" of 12*60 minutes from the number of
- minutes since the epoch, before dividing by the time period (effectively
- making "our" epoch start at Jan 1, 1970 12:00UTC).
+ period. However, we want our time periods to start at a regular offset
+ from the SRV voting schedule, so we subtract a "rotation time offset"
+ of 12 voting periods from the number of minutes since the epoch, before
+ dividing by the time period (effectively making "our" epoch start at Jan
+ 1, 1970 12:00UTC when the voting period is 1 hour.)
Example: If the current time is 2016-04-13 11:15:01 UTC, making the seconds
since the epoch 1460546101, and the number of minutes since the epoch
@@ -1226,7 +1246,7 @@ Table of contents:
And here is what the hidden service computes:
SECRET_SEED = x25519(hs_y, client_X)
- KEYS = KDF(subcredential | SECRET_SEED, 40)
+ KEYS = KDF(N_hs_subcred | SECRET_SEED, 40)
CLIENT-ID = fist 8 bytes of KEYS
COOKIE-KEY = last 32 bytes of KEYS
@@ -1251,6 +1271,13 @@ Table of contents:
discussed in [HS-DESC-SECOND-LAYER] below. The blob is base64 encoded
and enclosed in -----BEGIN MESSAGE---- and ----END MESSAGE---- wrappers.
+
+ Compatibility note: The C Tor implementation does not include a final
+ newline when generating this first-layer-plaintext section; other
+ implementations MUST accept this section even if it is missing its final
+ newline. Other implementations MAY generate this section without a final
+ newline themselves, to avoid being distinguishable from C tor.
+
2.5.1.3. Client behavior [FIRST-LAYER-CLIENT-BEHAVIOR]
The goal of clients at this stage is to decrypt the "encrypted" field as
@@ -1378,22 +1405,48 @@ Table of contents:
key of the introduction point Tor node used for the ntor handshake
when a client extends to it.
+ "onion-key" SP KeyType SP key.. NL
+
+ [Any number of times]
+
+ Implementations should accept other types of onion keys using this
+ syntax (where "KeyType" is some string other than "ntor");
+ unrecognized key types should be ignored.
+
"auth-key" NL certificate NL
[Exactly once per introduction point]
- The certificate is a proposal 220 certificate wrapped in "-----BEGIN
- ED25519 CERT-----" cross-certifying the introduction point
- authentication key using the descriptor signing key. The introduction
- point authentication key is included in the mandatory signing-key
- extension. The certificate type must be [09].
+ The certificate is a proposal 220 certificate wrapped in
+ "-----BEGIN ED25519 CERT-----". It contains the introduction
+ point authentication key (`KP_hs_intro_tid`), signed by
+ the descriptor signing key (`KP_hs_desc_sign`). The
+ certificate type must be [09], and the signing key extension
+ is mandatory.
+
+ NOTE: This certificate was originally intended to be
+ constructed the other way around: the signing and signed keys
+ are meant to be reversed. However, C tor implemented it
+ backwards, and other implementations now need to do the same
+ in order to conform. (Since this section is inside the
+ descriptor, which is _already_ signed by `KP_hs_desc_sign`,
+ the verification aspect of this certificate serves no point in
+ its current form.)
"enc-key" SP "ntor" SP key NL
[Exactly once per introduction point]
The key is a base64 encoded curve25519 public key used to encrypt
- the introduction request to service.
+ the introduction request to service. (`KP_hs_intro_ntor`)
+
+ "enc-key" SP KeyType SP key.. NL
+
+ [Any number of times]
+
+ Implementations should accept other types of onion keys using this
+ syntax (where "KeyType" is some string other than "ntor");
+ unrecognized key types should be ignored.
"enc-key-cert" NL certificate NL
@@ -1402,13 +1455,22 @@ Table of contents:
Cross-certification of the encryption key using the descriptor
signing key.
- For "ntor" keys, certificate is a proposal 220 certificate wrapped
- in "-----BEGIN ED25519 CERT-----" armor, cross-certifying the
- descriptor signing key with the ed25519 equivalent of a curve25519
- public encryption key derived using the process in proposal 228
- appendix A. The certificate type must be [0B], and the signing-key
+ For "ntor" keys, certificate is a proposal 220 certificate
+ wrapped in "-----BEGIN ED25519 CERT-----" armor. The subject
+ key is the the ed25519 equivalent of a curve25519 public
+ encryption key (`KP_hs_intro_ntor`), with the ed25519 key
+ derived using the process in proposal 228 appendix A. The
+ signing key is the descriptor signing key (`KP_hs_desc_sign`).
+ The certificate type must be [0B], and the signing-key
extension is mandatory.
+ NOTE: As with "auth-key", this certificate was intended to be
+ constructed the other way around. However, for compatibility
+ with C tor, implementations need to construct it this way. It
+ serves even less point than "auth-key", however, since the
+ encryption key `KP_hs_intro_ntor` is already available from
+ the `enc-key` entry.
+
"legacy-key" NL key NL
[None or at most once per introduction point]
@@ -1459,7 +1521,7 @@ Table of contents:
descriptor even if the content of the descriptor hasn't changed.
(So that we don't leak whether the intro point list etc. changed)
- secret_input = SECRET_DATA | subcredential | INT_8(revision_counter)
+ secret_input = SECRET_DATA | N_hs_subcred | INT_8(revision_counter)
keys = KDF(secret_input | salt | STRING_CONSTANT, S_KEY_LEN + S_IV_LEN + MAC_KEY_LEN)
@@ -1920,7 +1982,7 @@ Table of contents:
and computes:
intro_secret_hs_input = EXP(B,x) | AUTH_KEY | X | B | PROTOID
- info = m_hsexpand | subcredential
+ info = m_hsexpand | N_hs_subcred
hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN)
ENC_KEY = hs_keys[0:S_KEY_LEN]
MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN]
@@ -1974,7 +2036,7 @@ Table of contents:
introduction point encryption key 'b' to compute:
intro_secret_hs_input = EXP(X,b) | AUTH_KEY | X | B | PROTOID
- info = m_hsexpand | subcredential
+ info = m_hsexpand | N_hs_subcred
hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN)
HS_DEC_KEY = hs_keys[0:S_KEY_LEN]
HS_MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN]