onion_ntor.c (12787B)
1 /* Copyright (c) 2012-2021, The Tor Project, Inc. */ 2 /* See LICENSE for licensing information */ 3 4 /** 5 * \file onion_ntor.c 6 * 7 * \brief Implementation for the ntor handshake. 8 * 9 * The ntor circuit-extension handshake was developed as a replacement 10 * for the old TAP handshake. It uses Elliptic-curve Diffie-Hellman and 11 * a hash function in order to perform a one-way authenticated key 12 * exchange. The ntor handshake is meant to replace the old "TAP" 13 * handshake. 14 * 15 * We instantiate ntor with curve25519, HMAC-SHA256, and HKDF. 16 * 17 * This handshake, like the other circuit-extension handshakes, is 18 * invoked from onion.c. 19 */ 20 21 #include "orconfig.h" 22 23 #define ONION_NTOR_PRIVATE 24 25 #include "lib/crypt_ops/crypto_cipher.h" 26 #include "lib/crypt_ops/crypto_digest.h" 27 #include "lib/crypt_ops/crypto_hkdf.h" 28 #include "lib/crypt_ops/crypto_util.h" 29 #include "lib/ctime/di_ops.h" 30 #include "lib/log/log.h" 31 #include "lib/log/util_bug.h" 32 #include "core/crypto/onion_ntor.h" 33 34 #include <string.h> 35 36 /** Free storage held in an ntor handshake state. */ 37 void 38 ntor_handshake_state_free_(ntor_handshake_state_t *state) 39 { 40 if (!state) 41 return; 42 memwipe(state, 0, sizeof(*state)); 43 tor_free(state); 44 } 45 46 /** Convenience function to represent HMAC_SHA256 as our instantiation of 47 * ntor's "tweaked hash'. Hash the <b>inp_len</b> bytes at <b>inp</b> into 48 * a DIGEST256_LEN-byte digest at <b>out</b>, with the hash changing 49 * depending on the value of <b>tweak</b>. */ 50 static void 51 h_tweak(uint8_t *out, 52 const uint8_t *inp, size_t inp_len, 53 const char *tweak) 54 { 55 size_t tweak_len = strlen(tweak); 56 crypto_hmac_sha256((char*)out, tweak, tweak_len, (const char*)inp, inp_len); 57 } 58 59 /** Wrapper around a set of tweak-values for use with the ntor handshake. */ 60 typedef struct tweakset_t { 61 const char *t_mac; 62 const char *t_key; 63 const char *t_verify; 64 const char *m_expand; 65 } tweakset_t; 66 67 /** The tweaks to be used with our handshake. */ 68 static const tweakset_t proto1_tweaks = { 69 #define PROTOID "ntor-curve25519-sha256-1" 70 #define PROTOID_LEN 24 71 PROTOID ":mac", 72 PROTOID ":key_extract", 73 PROTOID ":verify", 74 PROTOID ":key_expand" 75 }; 76 77 /** Convenience macro: copy <b>len</b> bytes from <b>inp</b> to <b>ptr</b>, 78 * and advance <b>ptr</b> by the number of bytes copied. */ 79 #define APPEND(ptr, inp, len) \ 80 STMT_BEGIN { \ 81 memcpy(ptr, (inp), (len)); \ 82 ptr += len; \ 83 } STMT_END 84 85 /** 86 * Compute the first client-side step of the ntor handshake for communicating 87 * with a server whose DIGEST_LEN-byte server identity is <b>router_id</b>, 88 * and whose onion key is <b>router_key</b>. Store the NTOR_ONIONSKIN_LEN-byte 89 * message in <b>onion_skin_out</b>, and store the handshake state in 90 * *<b>handshake_state_out</b>. Return 0 on success, -1 on failure. 91 */ 92 int 93 onion_skin_ntor_create(const uint8_t *router_id, 94 const curve25519_public_key_t *router_key, 95 ntor_handshake_state_t **handshake_state_out, 96 uint8_t *onion_skin_out) 97 { 98 ntor_handshake_state_t *state; 99 uint8_t *op; 100 101 state = tor_malloc_zero(sizeof(ntor_handshake_state_t)); 102 103 memcpy(state->router_id, router_id, DIGEST_LEN); 104 memcpy(&state->pubkey_B, router_key, sizeof(curve25519_public_key_t)); 105 if (curve25519_secret_key_generate(&state->seckey_x, 0) < 0) { 106 /* LCOV_EXCL_START 107 * Secret key generation should be unable to fail when the key isn't 108 * marked as "extra-strong" */ 109 tor_assert_nonfatal_unreached(); 110 tor_free(state); 111 return -1; 112 /* LCOV_EXCL_STOP */ 113 } 114 curve25519_public_key_generate(&state->pubkey_X, &state->seckey_x); 115 116 op = onion_skin_out; 117 APPEND(op, router_id, DIGEST_LEN); 118 APPEND(op, router_key->public_key, CURVE25519_PUBKEY_LEN); 119 APPEND(op, state->pubkey_X.public_key, CURVE25519_PUBKEY_LEN); 120 tor_assert(op == onion_skin_out + NTOR_ONIONSKIN_LEN); 121 122 *handshake_state_out = state; 123 124 return 0; 125 } 126 127 #define SERVER_STR "Server" 128 #define SERVER_STR_LEN 6 129 130 #define SECRET_INPUT_LEN (CURVE25519_PUBKEY_LEN * 3 + \ 131 CURVE25519_OUTPUT_LEN * 2 + \ 132 DIGEST_LEN + PROTOID_LEN) 133 #define AUTH_INPUT_LEN (DIGEST256_LEN + DIGEST_LEN + \ 134 CURVE25519_PUBKEY_LEN*3 + \ 135 PROTOID_LEN + SERVER_STR_LEN) 136 137 /** 138 * Perform the server side of an ntor handshake. Given an 139 * NTOR_ONIONSKIN_LEN-byte message in <b>onion_skin</b>, our own identity 140 * fingerprint as <b>my_node_id</b>, and an associative array mapping public 141 * onion keys to curve25519_keypair_t in <b>private_keys</b>, attempt to 142 * perform the handshake. Use <b>junk_keys</b> if present if the handshake 143 * indicates an unrecognized public key. Write an NTOR_REPLY_LEN-byte 144 * message to send back to the client into <b>handshake_reply_out</b>, and 145 * generate <b>key_out_len</b> bytes of key material in <b>key_out</b>. Return 146 * 0 on success, -1 on failure. 147 */ 148 int 149 onion_skin_ntor_server_handshake(const uint8_t *onion_skin, 150 const di_digest256_map_t *private_keys, 151 const curve25519_keypair_t *junk_keys, 152 const uint8_t *my_node_id, 153 uint8_t *handshake_reply_out, 154 uint8_t *key_out, 155 size_t key_out_len) 156 { 157 const tweakset_t *T = &proto1_tweaks; 158 /* Sensitive stack-allocated material. Kept in an anonymous struct to make 159 * it easy to wipe. */ 160 struct { 161 uint8_t secret_input[SECRET_INPUT_LEN]; 162 uint8_t auth_input[AUTH_INPUT_LEN]; 163 curve25519_public_key_t pubkey_X; 164 curve25519_secret_key_t seckey_y; 165 curve25519_public_key_t pubkey_Y; 166 uint8_t verify[DIGEST256_LEN]; 167 } s; 168 uint8_t *si = s.secret_input, *ai = s.auth_input; 169 const curve25519_keypair_t *keypair_bB; 170 int bad; 171 172 /* Decode the onion skin */ 173 /* XXXX Does this possible early-return business threaten our security? */ 174 if (tor_memneq(onion_skin, my_node_id, DIGEST_LEN)) 175 return -1; 176 /* Note that on key-not-found, we go through with this operation anyway, 177 * using "junk_keys". This will result in failed authentication, but won't 178 * leak whether we recognized the key. */ 179 keypair_bB = dimap_search(private_keys, onion_skin + DIGEST_LEN, 180 (void*)junk_keys); 181 if (!keypair_bB) 182 return -1; 183 184 memcpy(s.pubkey_X.public_key, onion_skin+DIGEST_LEN+DIGEST256_LEN, 185 CURVE25519_PUBKEY_LEN); 186 187 /* Make y, Y */ 188 curve25519_secret_key_generate(&s.seckey_y, 0); 189 curve25519_public_key_generate(&s.pubkey_Y, &s.seckey_y); 190 191 /* NOTE: If we ever use a group other than curve25519, or a different 192 * representation for its points, we may need to perform different or 193 * additional checks on X here and on Y in the client handshake, or lose our 194 * security properties. What checks we need would depend on the properties 195 * of the group and its representation. 196 * 197 * In short: if you use anything other than curve25519, this aspect of the 198 * code will need to be reconsidered carefully. */ 199 200 /* build secret_input */ 201 curve25519_handshake(si, &s.seckey_y, &s.pubkey_X); 202 bad = safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN); 203 si += CURVE25519_OUTPUT_LEN; 204 curve25519_handshake(si, &keypair_bB->seckey, &s.pubkey_X); 205 bad |= safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN); 206 si += CURVE25519_OUTPUT_LEN; 207 208 APPEND(si, my_node_id, DIGEST_LEN); 209 APPEND(si, keypair_bB->pubkey.public_key, CURVE25519_PUBKEY_LEN); 210 APPEND(si, s.pubkey_X.public_key, CURVE25519_PUBKEY_LEN); 211 APPEND(si, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN); 212 APPEND(si, PROTOID, PROTOID_LEN); 213 tor_assert(si == s.secret_input + sizeof(s.secret_input)); 214 215 /* Compute hashes of secret_input */ 216 h_tweak(s.verify, s.secret_input, sizeof(s.secret_input), T->t_verify); 217 218 /* Compute auth_input */ 219 APPEND(ai, s.verify, DIGEST256_LEN); 220 APPEND(ai, my_node_id, DIGEST_LEN); 221 APPEND(ai, keypair_bB->pubkey.public_key, CURVE25519_PUBKEY_LEN); 222 APPEND(ai, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN); 223 APPEND(ai, s.pubkey_X.public_key, CURVE25519_PUBKEY_LEN); 224 APPEND(ai, PROTOID, PROTOID_LEN); 225 APPEND(ai, SERVER_STR, SERVER_STR_LEN); 226 tor_assert(ai == s.auth_input + sizeof(s.auth_input)); 227 228 /* Build the reply */ 229 memcpy(handshake_reply_out, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN); 230 h_tweak(handshake_reply_out+CURVE25519_PUBKEY_LEN, 231 s.auth_input, sizeof(s.auth_input), 232 T->t_mac); 233 234 /* Generate the key material */ 235 crypto_expand_key_material_rfc5869_sha256( 236 s.secret_input, sizeof(s.secret_input), 237 (const uint8_t*)T->t_key, strlen(T->t_key), 238 (const uint8_t*)T->m_expand, strlen(T->m_expand), 239 key_out, key_out_len); 240 241 /* Wipe all of our local state */ 242 memwipe(&s, 0, sizeof(s)); 243 244 return bad ? -1 : 0; 245 } 246 247 /** 248 * Perform the final client side of the ntor handshake, using the state in 249 * <b>handshake_state</b> and the server's NTOR_REPLY_LEN-byte reply in 250 * <b>handshake_reply</b>. Generate <b>key_out_len</b> bytes of key material 251 * in <b>key_out</b>. Return 0 on success, -1 on failure. 252 */ 253 int 254 onion_skin_ntor_client_handshake( 255 const ntor_handshake_state_t *handshake_state, 256 const uint8_t *handshake_reply, 257 uint8_t *key_out, 258 size_t key_out_len, 259 const char **msg_out) 260 { 261 const tweakset_t *T = &proto1_tweaks; 262 /* Sensitive stack-allocated material. Kept in an anonymous struct to make 263 * it easy to wipe. */ 264 struct { 265 curve25519_public_key_t pubkey_Y; 266 uint8_t secret_input[SECRET_INPUT_LEN]; 267 uint8_t verify[DIGEST256_LEN]; 268 uint8_t auth_input[AUTH_INPUT_LEN]; 269 uint8_t auth[DIGEST256_LEN]; 270 } s; 271 uint8_t *ai = s.auth_input, *si = s.secret_input; 272 const uint8_t *auth_candidate; 273 int bad; 274 275 /* Decode input */ 276 memcpy(s.pubkey_Y.public_key, handshake_reply, CURVE25519_PUBKEY_LEN); 277 auth_candidate = handshake_reply + CURVE25519_PUBKEY_LEN; 278 279 /* See note in server_handshake above about checking points. The 280 * circumstances under which we'd need to check Y for membership are 281 * different than those under which we'd be checking X. */ 282 283 /* Compute secret_input */ 284 curve25519_handshake(si, &handshake_state->seckey_x, &s.pubkey_Y); 285 bad = safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN); 286 si += CURVE25519_OUTPUT_LEN; 287 curve25519_handshake(si, &handshake_state->seckey_x, 288 &handshake_state->pubkey_B); 289 bad |= (safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN) << 1); 290 si += CURVE25519_OUTPUT_LEN; 291 APPEND(si, handshake_state->router_id, DIGEST_LEN); 292 APPEND(si, handshake_state->pubkey_B.public_key, CURVE25519_PUBKEY_LEN); 293 APPEND(si, handshake_state->pubkey_X.public_key, CURVE25519_PUBKEY_LEN); 294 APPEND(si, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN); 295 APPEND(si, PROTOID, PROTOID_LEN); 296 tor_assert(si == s.secret_input + sizeof(s.secret_input)); 297 298 /* Compute verify from secret_input */ 299 h_tweak(s.verify, s.secret_input, sizeof(s.secret_input), T->t_verify); 300 301 /* Compute auth_input */ 302 APPEND(ai, s.verify, DIGEST256_LEN); 303 APPEND(ai, handshake_state->router_id, DIGEST_LEN); 304 APPEND(ai, handshake_state->pubkey_B.public_key, CURVE25519_PUBKEY_LEN); 305 APPEND(ai, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN); 306 APPEND(ai, handshake_state->pubkey_X.public_key, CURVE25519_PUBKEY_LEN); 307 APPEND(ai, PROTOID, PROTOID_LEN); 308 APPEND(ai, SERVER_STR, SERVER_STR_LEN); 309 tor_assert(ai == s.auth_input + sizeof(s.auth_input)); 310 311 /* Compute auth */ 312 h_tweak(s.auth, s.auth_input, sizeof(s.auth_input), T->t_mac); 313 314 bad |= (tor_memneq(s.auth, auth_candidate, DIGEST256_LEN) << 2); 315 316 crypto_expand_key_material_rfc5869_sha256( 317 s.secret_input, sizeof(s.secret_input), 318 (const uint8_t*)T->t_key, strlen(T->t_key), 319 (const uint8_t*)T->m_expand, strlen(T->m_expand), 320 key_out, key_out_len); 321 322 memwipe(&s, 0, sizeof(s)); 323 324 if (bad) { 325 if (bad & 4) { 326 if (msg_out) 327 *msg_out = NULL; /* Don't report this one; we probably just had the 328 * wrong onion key.*/ 329 log_fn(LOG_INFO, LD_PROTOCOL, 330 "Invalid result from curve25519 handshake: %d", bad); 331 } 332 if (bad & 3) { 333 if (msg_out) 334 *msg_out = "Zero output from curve25519 handshake"; 335 log_fn(LOG_WARN, LD_PROTOCOL, 336 "Invalid result from curve25519 handshake: %d", bad); 337 } 338 } 339 340 return bad ? -1 : 0; 341 }