LCOV - code coverage report
Current view: top level - core/crypto - onion_ntor.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 104 104 100.0 %
Date: 2021-11-24 03:28:48 Functions: 5 5 100.0 %

          Line data    Source code
       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           1 : ntor_handshake_state_free_(ntor_handshake_state_t *state)
      39             : {
      40           1 :   if (!state)
      41             :     return;
      42           1 :   memwipe(state, 0, sizeof(*state));
      43           1 :   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           6 : h_tweak(uint8_t *out,
      52             :         const uint8_t *inp, size_t inp_len,
      53             :         const char *tweak)
      54             : {
      55           6 :   size_t tweak_len = strlen(tweak);
      56           6 :   crypto_hmac_sha256((char*)out, tweak, tweak_len, (const char*)inp, inp_len);
      57           6 : }
      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           1 : 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           1 :   ntor_handshake_state_t *state;
      99           1 :   uint8_t *op;
     100             : 
     101           1 :   state = tor_malloc_zero(sizeof(ntor_handshake_state_t));
     102             : 
     103           1 :   memcpy(state->router_id, router_id, DIGEST_LEN);
     104           1 :   memcpy(&state->pubkey_B, router_key, sizeof(curve25519_public_key_t));
     105           1 :   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           1 :   curve25519_public_key_generate(&state->pubkey_X, &state->seckey_x);
     115             : 
     116           1 :   op = onion_skin_out;
     117           1 :   APPEND(op, router_id, DIGEST_LEN);
     118           1 :   APPEND(op, router_key->public_key, CURVE25519_PUBKEY_LEN);
     119           1 :   APPEND(op, state->pubkey_X.public_key, CURVE25519_PUBKEY_LEN);
     120           1 :   tor_assert(op == onion_skin_out + NTOR_ONIONSKIN_LEN);
     121             : 
     122           1 :   *handshake_state_out = state;
     123             : 
     124           1 :   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           1 : 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           1 :   const tweakset_t *T = &proto1_tweaks;
     158             :   /* Sensitive stack-allocated material. Kept in an anonymous struct to make
     159             :    * it easy to wipe. */
     160           1 :   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           1 :   uint8_t *si = s.secret_input, *ai = s.auth_input;
     169           1 :   const curve25519_keypair_t *keypair_bB;
     170           1 :   int bad;
     171             : 
     172             :   /* Decode the onion skin */
     173             :   /* XXXX Does this possible early-return business threaten our security? */
     174           1 :   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           1 :   keypair_bB = dimap_search(private_keys, onion_skin + DIGEST_LEN,
     180             :                             (void*)junk_keys);
     181           1 :   if (!keypair_bB)
     182             :     return -1;
     183             : 
     184           1 :   memcpy(s.pubkey_X.public_key, onion_skin+DIGEST_LEN+DIGEST256_LEN,
     185             :          CURVE25519_PUBKEY_LEN);
     186             : 
     187             :   /* Make y, Y */
     188           1 :   curve25519_secret_key_generate(&s.seckey_y, 0);
     189           1 :   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           1 :   curve25519_handshake(si, &s.seckey_y, &s.pubkey_X);
     202           1 :   bad = safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN);
     203           1 :   si += CURVE25519_OUTPUT_LEN;
     204           1 :   curve25519_handshake(si, &keypair_bB->seckey, &s.pubkey_X);
     205           1 :   bad |= safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN);
     206           1 :   si += CURVE25519_OUTPUT_LEN;
     207             : 
     208           1 :   APPEND(si, my_node_id, DIGEST_LEN);
     209           1 :   APPEND(si, keypair_bB->pubkey.public_key, CURVE25519_PUBKEY_LEN);
     210           1 :   APPEND(si, s.pubkey_X.public_key, CURVE25519_PUBKEY_LEN);
     211           1 :   APPEND(si, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
     212           1 :   APPEND(si, PROTOID, PROTOID_LEN);
     213           1 :   tor_assert(si == s.secret_input + sizeof(s.secret_input));
     214             : 
     215             :   /* Compute hashes of secret_input */
     216           1 :   h_tweak(s.verify, s.secret_input, sizeof(s.secret_input), T->t_verify);
     217             : 
     218             :   /* Compute auth_input */
     219           1 :   APPEND(ai, s.verify, DIGEST256_LEN);
     220           1 :   APPEND(ai, my_node_id, DIGEST_LEN);
     221           1 :   APPEND(ai, keypair_bB->pubkey.public_key, CURVE25519_PUBKEY_LEN);
     222           1 :   APPEND(ai, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
     223           1 :   APPEND(ai, s.pubkey_X.public_key, CURVE25519_PUBKEY_LEN);
     224           1 :   APPEND(ai, PROTOID, PROTOID_LEN);
     225           1 :   APPEND(ai, SERVER_STR, SERVER_STR_LEN);
     226           1 :   tor_assert(ai == s.auth_input + sizeof(s.auth_input));
     227             : 
     228             :   /* Build the reply */
     229           1 :   memcpy(handshake_reply_out, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
     230           1 :   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           1 :   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           1 :   memwipe(&s, 0, sizeof(s));
     243             : 
     244           1 :   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           2 : 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           2 :   const tweakset_t *T = &proto1_tweaks;
     262             :   /* Sensitive stack-allocated material. Kept in an anonymous struct to make
     263             :    * it easy to wipe. */
     264           2 :   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           2 :   uint8_t *ai = s.auth_input, *si = s.secret_input;
     272           2 :   const uint8_t *auth_candidate;
     273           2 :   int bad;
     274             : 
     275             :   /* Decode input */
     276           2 :   memcpy(s.pubkey_Y.public_key, handshake_reply, CURVE25519_PUBKEY_LEN);
     277           2 :   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           2 :   curve25519_handshake(si, &handshake_state->seckey_x, &s.pubkey_Y);
     285           2 :   bad = safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN);
     286           2 :   si += CURVE25519_OUTPUT_LEN;
     287           2 :   curve25519_handshake(si, &handshake_state->seckey_x,
     288             :                        &handshake_state->pubkey_B);
     289           2 :   bad |= (safe_mem_is_zero(si, CURVE25519_OUTPUT_LEN) << 1);
     290           2 :   si += CURVE25519_OUTPUT_LEN;
     291           2 :   APPEND(si, handshake_state->router_id, DIGEST_LEN);
     292           2 :   APPEND(si, handshake_state->pubkey_B.public_key, CURVE25519_PUBKEY_LEN);
     293           2 :   APPEND(si, handshake_state->pubkey_X.public_key, CURVE25519_PUBKEY_LEN);
     294           2 :   APPEND(si, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
     295           2 :   APPEND(si, PROTOID, PROTOID_LEN);
     296           2 :   tor_assert(si == s.secret_input + sizeof(s.secret_input));
     297             : 
     298             :   /* Compute verify from secret_input */
     299           2 :   h_tweak(s.verify, s.secret_input, sizeof(s.secret_input), T->t_verify);
     300             : 
     301             :   /* Compute auth_input */
     302           2 :   APPEND(ai, s.verify, DIGEST256_LEN);
     303           2 :   APPEND(ai, handshake_state->router_id, DIGEST_LEN);
     304           2 :   APPEND(ai, handshake_state->pubkey_B.public_key, CURVE25519_PUBKEY_LEN);
     305           2 :   APPEND(ai, s.pubkey_Y.public_key, CURVE25519_PUBKEY_LEN);
     306           2 :   APPEND(ai, handshake_state->pubkey_X.public_key, CURVE25519_PUBKEY_LEN);
     307           2 :   APPEND(ai, PROTOID, PROTOID_LEN);
     308           2 :   APPEND(ai, SERVER_STR, SERVER_STR_LEN);
     309           2 :   tor_assert(ai == s.auth_input + sizeof(s.auth_input));
     310             : 
     311             :   /* Compute auth */
     312           2 :   h_tweak(s.auth, s.auth_input, sizeof(s.auth_input), T->t_mac);
     313             : 
     314           2 :   bad |= (tor_memneq(s.auth, auth_candidate, DIGEST256_LEN) << 2);
     315             : 
     316           2 :   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           2 :   memwipe(&s, 0, sizeof(s));
     323             : 
     324           2 :   if (bad) {
     325           1 :     if (bad & 4) {
     326           1 :       if (msg_out)
     327           1 :         *msg_out = NULL; /* Don't report this one; we probably just had the
     328             :                           * wrong onion key.*/
     329           1 :       log_fn(LOG_INFO, LD_PROTOCOL,
     330             :              "Invalid result from curve25519 handshake: %d", bad);
     331             :     }
     332           1 :     if (bad & 3) {
     333           1 :       if (msg_out)
     334           1 :         *msg_out = "Zero output from curve25519 handshake";
     335           1 :       log_fn(LOG_WARN, LD_PROTOCOL,
     336             :              "Invalid result from curve25519 handshake: %d", bad);
     337             :     }
     338             :   }
     339             : 
     340           2 :   return bad ? -1 : 0;
     341             : }

Generated by: LCOV version 1.14