Bug 6572: Use timestamp_created for liveness sanity checks.
[tor.git] / src / or / onion_tap.c
blob3782e75abf6bf16d04afdbcf43e9c0eef46306dd
1 /* Copyright (c) 2001 Matej Pfajfar.
2 * Copyright (c) 2001-2004, Roger Dingledine.
3 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
4 * Copyright (c) 2007-2013, The Tor Project, Inc. */
5 /* See LICENSE for licensing information */
7 /**
8 * \file onion_tap.c
9 * \brief Functions to implement the original Tor circuit extension handshake
10 * (a.k.a TAP).
12 * We didn't call it "TAP" ourselves -- Ian Goldberg named it in "On the
13 * Security of the Tor Authentication Protocol". (Spoiler: it's secure, but
14 * its security is kind of fragile and implementation dependent. Never modify
15 * this implementation without reading and understanding that paper at least.)
16 **/
18 #include "or.h"
19 #include "config.h"
20 #include "onion_tap.h"
21 #include "rephist.h"
23 /*----------------------------------------------------------------------*/
25 /** Given a router's 128 byte public key,
26 * stores the following in onion_skin_out:
27 * - [42 bytes] OAEP padding
28 * - [16 bytes] Symmetric key for encrypting blob past RSA
29 * - [70 bytes] g^x part 1 (inside the RSA)
30 * - [58 bytes] g^x part 2 (symmetrically encrypted)
32 * Stores the DH private key into handshake_state_out for later completion
33 * of the handshake.
35 * The meeting point/cookies and auth are zeroed out for now.
37 int
38 onion_skin_TAP_create(crypto_pk_t *dest_router_key,
39 crypto_dh_t **handshake_state_out,
40 char *onion_skin_out) /* TAP_ONIONSKIN_CHALLENGE_LEN bytes */
42 char challenge[DH_KEY_LEN];
43 crypto_dh_t *dh = NULL;
44 int dhbytes, pkbytes;
46 tor_assert(dest_router_key);
47 tor_assert(handshake_state_out);
48 tor_assert(onion_skin_out);
49 *handshake_state_out = NULL;
50 memset(onion_skin_out, 0, TAP_ONIONSKIN_CHALLENGE_LEN);
52 if (!(dh = crypto_dh_new(DH_TYPE_CIRCUIT)))
53 goto err;
55 dhbytes = crypto_dh_get_bytes(dh);
56 pkbytes = (int) crypto_pk_keysize(dest_router_key);
57 tor_assert(dhbytes == 128);
58 tor_assert(pkbytes == 128);
60 if (crypto_dh_get_public(dh, challenge, dhbytes))
61 goto err;
63 note_crypto_pk_op(ENC_ONIONSKIN);
65 /* set meeting point, meeting cookie, etc here. Leave zero for now. */
66 if (crypto_pk_public_hybrid_encrypt(dest_router_key, onion_skin_out,
67 TAP_ONIONSKIN_CHALLENGE_LEN,
68 challenge, DH_KEY_LEN,
69 PK_PKCS1_OAEP_PADDING, 1)<0)
70 goto err;
72 memwipe(challenge, 0, sizeof(challenge));
73 *handshake_state_out = dh;
75 return 0;
76 err:
77 memwipe(challenge, 0, sizeof(challenge));
78 if (dh) crypto_dh_free(dh);
79 return -1;
82 /** Given an encrypted DH public key as generated by onion_skin_create,
83 * and the private key for this onion router, generate the reply (128-byte
84 * DH plus the first 20 bytes of shared key material), and store the
85 * next key_out_len bytes of key material in key_out.
87 int
88 onion_skin_TAP_server_handshake(
89 /*TAP_ONIONSKIN_CHALLENGE_LEN*/
90 const char *onion_skin,
91 crypto_pk_t *private_key,
92 crypto_pk_t *prev_private_key,
93 /*TAP_ONIONSKIN_REPLY_LEN*/
94 char *handshake_reply_out,
95 char *key_out,
96 size_t key_out_len)
98 char challenge[TAP_ONIONSKIN_CHALLENGE_LEN];
99 crypto_dh_t *dh = NULL;
100 ssize_t len;
101 char *key_material=NULL;
102 size_t key_material_len=0;
103 int i;
104 crypto_pk_t *k;
106 len = -1;
107 for (i=0;i<2;++i) {
108 k = i==0?private_key:prev_private_key;
109 if (!k)
110 break;
111 note_crypto_pk_op(DEC_ONIONSKIN);
112 len = crypto_pk_private_hybrid_decrypt(k, challenge,
113 TAP_ONIONSKIN_CHALLENGE_LEN,
114 onion_skin,
115 TAP_ONIONSKIN_CHALLENGE_LEN,
116 PK_PKCS1_OAEP_PADDING,0);
117 if (len>0)
118 break;
120 if (len<0) {
121 log_info(LD_PROTOCOL,
122 "Couldn't decrypt onionskin: client may be using old onion key");
123 goto err;
124 } else if (len != DH_KEY_LEN) {
125 log_warn(LD_PROTOCOL, "Unexpected onionskin length after decryption: %ld",
126 (long)len);
127 goto err;
130 dh = crypto_dh_new(DH_TYPE_CIRCUIT);
131 if (!dh) {
132 log_warn(LD_BUG, "Couldn't allocate DH key");
133 goto err;
135 if (crypto_dh_get_public(dh, handshake_reply_out, DH_KEY_LEN)) {
136 log_info(LD_GENERAL, "crypto_dh_get_public failed.");
137 goto err;
140 key_material_len = DIGEST_LEN+key_out_len;
141 key_material = tor_malloc(key_material_len);
142 len = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, dh, challenge,
143 DH_KEY_LEN, key_material,
144 key_material_len);
145 if (len < 0) {
146 log_info(LD_GENERAL, "crypto_dh_compute_secret failed.");
147 goto err;
150 /* send back H(K|0) as proof that we learned K. */
151 memcpy(handshake_reply_out+DH_KEY_LEN, key_material, DIGEST_LEN);
153 /* use the rest of the key material for our shared keys, digests, etc */
154 memcpy(key_out, key_material+DIGEST_LEN, key_out_len);
156 memwipe(challenge, 0, sizeof(challenge));
157 memwipe(key_material, 0, key_material_len);
158 tor_free(key_material);
159 crypto_dh_free(dh);
160 return 0;
161 err:
162 memwipe(challenge, 0, sizeof(challenge));
163 if (key_material) {
164 memwipe(key_material, 0, key_material_len);
165 tor_free(key_material);
167 if (dh) crypto_dh_free(dh);
169 return -1;
172 /** Finish the client side of the DH handshake.
173 * Given the 128 byte DH reply + 20 byte hash as generated by
174 * onion_skin_server_handshake and the handshake state generated by
175 * onion_skin_create, verify H(K) with the first 20 bytes of shared
176 * key material, then generate key_out_len more bytes of shared key
177 * material and store them in key_out.
179 * After the invocation, call crypto_dh_free on handshake_state.
182 onion_skin_TAP_client_handshake(crypto_dh_t *handshake_state,
183 const char *handshake_reply, /* TAP_ONIONSKIN_REPLY_LEN bytes */
184 char *key_out,
185 size_t key_out_len)
187 ssize_t len;
188 char *key_material=NULL;
189 size_t key_material_len;
190 tor_assert(crypto_dh_get_bytes(handshake_state) == DH_KEY_LEN);
192 key_material_len = DIGEST_LEN + key_out_len;
193 key_material = tor_malloc(key_material_len);
194 len = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, handshake_state,
195 handshake_reply, DH_KEY_LEN, key_material,
196 key_material_len);
197 if (len < 0)
198 goto err;
200 if (tor_memneq(key_material, handshake_reply+DH_KEY_LEN, DIGEST_LEN)) {
201 /* H(K) does *not* match. Something fishy. */
202 log_warn(LD_PROTOCOL,"Digest DOES NOT MATCH on onion handshake. "
203 "Bug or attack.");
204 goto err;
207 /* use the rest of the key material for our shared keys, digests, etc */
208 memcpy(key_out, key_material+DIGEST_LEN, key_out_len);
210 memwipe(key_material, 0, key_material_len);
211 tor_free(key_material);
212 return 0;
213 err:
214 memwipe(key_material, 0, key_material_len);
215 tor_free(key_material);
216 return -1;