tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

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 }