Line data Source code
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-2021, The Tor Project, Inc. */
5 : /* See LICENSE for licensing information */
6 :
7 : /**
8 : * \file onion_tap.c
9 : * \brief Functions to implement the original Tor circuit extension handshake
10 : * (a.k.a TAP).
11 : *
12 : * The "TAP" handshake is the first one that was widely used in Tor: It
13 : * combines RSA1024-OAEP and AES128-CTR to perform a hybrid encryption over
14 : * the first message DH1024 key exchange. (The RSA-encrypted part of the
15 : * encryption is authenticated; the AES-encrypted part isn't. This was
16 : * not a smart choice.)
17 : *
18 : * We didn't call it "TAP" ourselves -- Ian Goldberg named it in "On the
19 : * Security of the Tor Authentication Protocol". (Spoiler: it's secure, but
20 : * its security is kind of fragile and implementation dependent. Never modify
21 : * this implementation without reading and understanding that paper at least.)
22 : *
23 : * We have deprecated TAP since the ntor handshake came into general use. It
24 : * is still used for hidden service IP and RP connections, however.
25 : *
26 : * This handshake, like the other circuit-extension handshakes, is
27 : * invoked from onion.c.
28 : **/
29 :
30 : #include "core/or/or.h"
31 : #include "app/config/config.h"
32 : #include "lib/crypt_ops/crypto_dh.h"
33 : #include "lib/crypt_ops/crypto_rand.h"
34 : #include "lib/crypt_ops/crypto_util.h"
35 : #include "core/crypto/onion_tap.h"
36 : #include "feature/stats/rephist.h"
37 :
38 : /*----------------------------------------------------------------------*/
39 :
40 : /** Given a router's 128 byte public key,
41 : * stores the following in onion_skin_out:
42 : * - [42 bytes] OAEP padding
43 : * - [16 bytes] Symmetric key for encrypting blob past RSA
44 : * - [70 bytes] g^x part 1 (inside the RSA)
45 : * - [58 bytes] g^x part 2 (symmetrically encrypted)
46 : *
47 : * Stores the DH private key into handshake_state_out for later completion
48 : * of the handshake.
49 : *
50 : * The meeting point/cookies and auth are zeroed out for now.
51 : */
52 : int
53 2 : onion_skin_TAP_create(crypto_pk_t *dest_router_key,
54 : crypto_dh_t **handshake_state_out,
55 : char *onion_skin_out) /* TAP_ONIONSKIN_CHALLENGE_LEN bytes */
56 : {
57 2 : char challenge[DH1024_KEY_LEN];
58 2 : crypto_dh_t *dh = NULL;
59 2 : int dhbytes, pkbytes;
60 :
61 2 : tor_assert(dest_router_key);
62 2 : tor_assert(handshake_state_out);
63 2 : tor_assert(onion_skin_out);
64 2 : *handshake_state_out = NULL;
65 2 : memset(onion_skin_out, 0, TAP_ONIONSKIN_CHALLENGE_LEN);
66 :
67 2 : if (!(dh = crypto_dh_new(DH_TYPE_CIRCUIT)))
68 0 : goto err;
69 :
70 2 : dhbytes = crypto_dh_get_bytes(dh);
71 2 : pkbytes = (int) crypto_pk_keysize(dest_router_key);
72 2 : tor_assert(dhbytes == 128);
73 2 : tor_assert(pkbytes == 128);
74 :
75 2 : if (crypto_dh_get_public(dh, challenge, dhbytes))
76 0 : goto err;
77 :
78 : /* set meeting point, meeting cookie, etc here. Leave zero for now. */
79 2 : if (crypto_pk_obsolete_public_hybrid_encrypt(dest_router_key, onion_skin_out,
80 : TAP_ONIONSKIN_CHALLENGE_LEN,
81 : challenge, DH1024_KEY_LEN,
82 : PK_PKCS1_OAEP_PADDING, 1)<0)
83 0 : goto err;
84 :
85 2 : memwipe(challenge, 0, sizeof(challenge));
86 2 : *handshake_state_out = dh;
87 :
88 2 : return 0;
89 0 : err:
90 : /* LCOV_EXCL_START
91 : * We only get here if RSA encryption fails or DH keygen fails. Those
92 : * shouldn't be possible. */
93 : memwipe(challenge, 0, sizeof(challenge));
94 : if (dh) crypto_dh_free(dh);
95 : return -1;
96 : /* LCOV_EXCL_STOP */
97 : }
98 :
99 : /** Given an encrypted DH public key as generated by onion_skin_create,
100 : * and the private key for this onion router, generate the reply (128-byte
101 : * DH plus the first 20 bytes of shared key material), and store the
102 : * next key_out_len bytes of key material in key_out.
103 : */
104 : int
105 8 : onion_skin_TAP_server_handshake(
106 : /*TAP_ONIONSKIN_CHALLENGE_LEN*/
107 : const char *onion_skin,
108 : crypto_pk_t *private_key,
109 : crypto_pk_t *prev_private_key,
110 : /*TAP_ONIONSKIN_REPLY_LEN*/
111 : char *handshake_reply_out,
112 : char *key_out,
113 : size_t key_out_len)
114 : {
115 8 : char challenge[TAP_ONIONSKIN_CHALLENGE_LEN];
116 8 : crypto_dh_t *dh = NULL;
117 8 : ssize_t len;
118 8 : char *key_material=NULL;
119 8 : size_t key_material_len=0;
120 8 : int i;
121 8 : crypto_pk_t *k;
122 :
123 8 : len = -1;
124 11 : for (i=0;i<2;++i) {
125 11 : k = i==0?private_key:prev_private_key;
126 11 : if (!k)
127 : break;
128 9 : len = crypto_pk_obsolete_private_hybrid_decrypt(k, challenge,
129 : TAP_ONIONSKIN_CHALLENGE_LEN,
130 : onion_skin,
131 : TAP_ONIONSKIN_CHALLENGE_LEN,
132 : PK_PKCS1_OAEP_PADDING,0);
133 9 : if (len>0)
134 : break;
135 : }
136 8 : if (len<0) {
137 2 : log_info(LD_PROTOCOL,
138 : "Couldn't decrypt onionskin: client may be using old onion key");
139 2 : goto err;
140 6 : } else if (len != DH1024_KEY_LEN) {
141 1 : log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
142 : "Unexpected onionskin length after decryption: %ld",
143 : (long)len);
144 1 : goto err;
145 : }
146 :
147 5 : dh = crypto_dh_new(DH_TYPE_CIRCUIT);
148 5 : if (!dh) {
149 : /* LCOV_EXCL_START
150 : * Failure to allocate a DH key should be impossible.
151 : */
152 : log_warn(LD_BUG, "Couldn't allocate DH key");
153 : goto err;
154 : /* LCOV_EXCL_STOP */
155 : }
156 5 : if (crypto_dh_get_public(dh, handshake_reply_out, DH1024_KEY_LEN)) {
157 : /* LCOV_EXCL_START
158 : * This can only fail if the length of the key we just allocated is too
159 : * big. That should be impossible. */
160 : log_info(LD_GENERAL, "crypto_dh_get_public failed.");
161 : goto err;
162 : /* LCOV_EXCL_STOP */
163 : }
164 :
165 5 : key_material_len = DIGEST_LEN+key_out_len;
166 5 : key_material = tor_malloc(key_material_len);
167 5 : len = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, dh, challenge,
168 : DH1024_KEY_LEN, key_material,
169 : key_material_len);
170 5 : if (len < 0) {
171 1 : log_info(LD_GENERAL, "crypto_dh_compute_secret failed.");
172 1 : goto err;
173 : }
174 :
175 : /* send back H(K|0) as proof that we learned K. */
176 4 : memcpy(handshake_reply_out+DH1024_KEY_LEN, key_material, DIGEST_LEN);
177 :
178 : /* use the rest of the key material for our shared keys, digests, etc */
179 4 : memcpy(key_out, key_material+DIGEST_LEN, key_out_len);
180 :
181 4 : memwipe(challenge, 0, sizeof(challenge));
182 4 : memwipe(key_material, 0, key_material_len);
183 4 : tor_free(key_material);
184 4 : crypto_dh_free(dh);
185 4 : return 0;
186 4 : err:
187 4 : memwipe(challenge, 0, sizeof(challenge));
188 4 : if (key_material) {
189 1 : memwipe(key_material, 0, key_material_len);
190 1 : tor_free(key_material);
191 : }
192 4 : if (dh) crypto_dh_free(dh);
193 :
194 : return -1;
195 : }
196 :
197 : /** Finish the client side of the DH handshake.
198 : * Given the 128 byte DH reply + 20 byte hash as generated by
199 : * onion_skin_server_handshake and the handshake state generated by
200 : * onion_skin_create, verify H(K) with the first 20 bytes of shared
201 : * key material, then generate key_out_len more bytes of shared key
202 : * material and store them in key_out.
203 : *
204 : * After the invocation, call crypto_dh_free on handshake_state.
205 : */
206 : int
207 6 : onion_skin_TAP_client_handshake(crypto_dh_t *handshake_state,
208 : const char *handshake_reply, /* TAP_ONIONSKIN_REPLY_LEN bytes */
209 : char *key_out,
210 : size_t key_out_len,
211 : const char **msg_out)
212 : {
213 6 : ssize_t len;
214 6 : char *key_material=NULL;
215 6 : size_t key_material_len;
216 6 : tor_assert(crypto_dh_get_bytes(handshake_state) == DH1024_KEY_LEN);
217 :
218 6 : key_material_len = DIGEST_LEN + key_out_len;
219 6 : key_material = tor_malloc(key_material_len);
220 6 : len = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, handshake_state,
221 : handshake_reply, DH1024_KEY_LEN, key_material,
222 : key_material_len);
223 6 : if (len < 0) {
224 1 : if (msg_out)
225 1 : *msg_out = "DH computation failed.";
226 1 : goto err;
227 : }
228 :
229 5 : if (tor_memneq(key_material, handshake_reply+DH1024_KEY_LEN, DIGEST_LEN)) {
230 : /* H(K) does *not* match. Something fishy. */
231 1 : if (msg_out)
232 1 : *msg_out = "Digest DOES NOT MATCH on onion handshake. Bug or attack.";
233 1 : goto err;
234 : }
235 :
236 : /* use the rest of the key material for our shared keys, digests, etc */
237 4 : memcpy(key_out, key_material+DIGEST_LEN, key_out_len);
238 :
239 4 : memwipe(key_material, 0, key_material_len);
240 4 : tor_free(key_material);
241 4 : return 0;
242 2 : err:
243 2 : memwipe(key_material, 0, key_material_len);
244 2 : tor_free(key_material);
245 2 : return -1;
246 : }
|