Commit f7e590df authored by Nick Mathewson's avatar Nick Mathewson 🥔
Browse files

Split onion.[ch] into onion{,_fast,_tap}.[ch]

I'm going to want a generic "onionskin" type and set of wrappers, and
for that, it will be helpful to isolate the different circuit creation
handshakes.  Now the original handshake is in onion_tap.[ch], the
CREATE_FAST handshake is in onion_fast.[ch], and onion.[ch] now
handles the onion queue.

This commit does nothing but move code and adjust header files.
parent 5b3dd161
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -27,7 +27,8 @@
#include "main.h"
#include "networkstatus.h"
#include "nodelist.h"
#include "onion.h"
#include "onion_tap.h"
#include "onion_fast.h"
#include "policies.h"
#include "transports.h"
#include "relay.h"
+2 −1
Original line number Diff line number Diff line
@@ -29,7 +29,8 @@
#include "cpuworker.h"
#include "hibernate.h"
#include "nodelist.h"
#include "onion.h"
//#include "onion.h"
#include "onion_fast.h"
#include "relay.h"
#include "router.h"
#include "routerlist.h"
+1 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include "cpuworker.h"
#include "main.h"
#include "onion.h"
#include "onion_tap.h"
#include "router.h"

/** The maximum number of cpuworker processes we will keep around. */
+4 −0
Original line number Diff line number Diff line
@@ -53,6 +53,8 @@ src_or_libtor_a_SOURCES = \
	src/or/networkstatus.c				\
	src/or/nodelist.c				\
	src/or/onion.c					\
	src/or/onion_fast.c				\
	src/or/onion_tap.c				\
	src/or/transports.c				\
	src/or/policies.c				\
	src/or/reasons.c				\
@@ -132,7 +134,9 @@ ORHEADERS = \
	src/or/nodelist.h				\
	src/or/ntmain.h					\
	src/or/onion.h					\
	src/or/onion_fast.h				\
	src/or/onion_ntor.h				\
	src/or/onion_tap.h				\
	src/or/or.h					\
	src/or/transports.h				\
	src/or/policies.h				\
+0 −275
Original line number Diff line number Diff line
@@ -157,281 +157,6 @@ onion_pending_remove(or_circuit_t *circ)
  tor_free(victim);
}

/*----------------------------------------------------------------------*/

/** Given a router's 128 byte public key,
 * stores the following in onion_skin_out:
 *   - [42 bytes] OAEP padding
 *   - [16 bytes] Symmetric key for encrypting blob past RSA
 *   - [70 bytes] g^x part 1 (inside the RSA)
 *   - [58 bytes] g^x part 2 (symmetrically encrypted)
 *
 * Stores the DH private key into handshake_state_out for later completion
 * of the handshake.
 *
 * The meeting point/cookies and auth are zeroed out for now.
 */
int
onion_skin_create(crypto_pk_t *dest_router_key,
                  crypto_dh_t **handshake_state_out,
                  char *onion_skin_out) /* ONIONSKIN_CHALLENGE_LEN bytes */
{
  char challenge[DH_KEY_LEN];
  crypto_dh_t *dh = NULL;
  int dhbytes, pkbytes;

  tor_assert(dest_router_key);
  tor_assert(handshake_state_out);
  tor_assert(onion_skin_out);
  *handshake_state_out = NULL;
  memset(onion_skin_out, 0, ONIONSKIN_CHALLENGE_LEN);

  if (!(dh = crypto_dh_new(DH_TYPE_CIRCUIT)))
    goto err;

  dhbytes = crypto_dh_get_bytes(dh);
  pkbytes = (int) crypto_pk_keysize(dest_router_key);
  tor_assert(dhbytes == 128);
  tor_assert(pkbytes == 128);

  if (crypto_dh_get_public(dh, challenge, dhbytes))
    goto err;

  note_crypto_pk_op(ENC_ONIONSKIN);

  /* set meeting point, meeting cookie, etc here. Leave zero for now. */
  if (crypto_pk_public_hybrid_encrypt(dest_router_key, onion_skin_out,
                                      ONIONSKIN_CHALLENGE_LEN,
                                      challenge, DH_KEY_LEN,
                                      PK_PKCS1_OAEP_PADDING, 1)<0)
    goto err;

  memwipe(challenge, 0, sizeof(challenge));
  *handshake_state_out = dh;

  return 0;
 err:
  memwipe(challenge, 0, sizeof(challenge));
  if (dh) crypto_dh_free(dh);
  return -1;
}

/** Given an encrypted DH public key as generated by onion_skin_create,
 * and the private key for this onion router, generate the reply (128-byte
 * DH plus the first 20 bytes of shared key material), and store the
 * next key_out_len bytes of key material in key_out.
 */
int
onion_skin_server_handshake(const char *onion_skin, /*ONIONSKIN_CHALLENGE_LEN*/
                            crypto_pk_t *private_key,
                            crypto_pk_t *prev_private_key,
                            char *handshake_reply_out, /*ONIONSKIN_REPLY_LEN*/
                            char *key_out,
                            size_t key_out_len)
{
  char challenge[ONIONSKIN_CHALLENGE_LEN];
  crypto_dh_t *dh = NULL;
  ssize_t len;
  char *key_material=NULL;
  size_t key_material_len=0;
  int i;
  crypto_pk_t *k;

  len = -1;
  for (i=0;i<2;++i) {
    k = i==0?private_key:prev_private_key;
    if (!k)
      break;
    note_crypto_pk_op(DEC_ONIONSKIN);
    len = crypto_pk_private_hybrid_decrypt(k, challenge,
                                           ONIONSKIN_CHALLENGE_LEN,
                                           onion_skin, ONIONSKIN_CHALLENGE_LEN,
                                           PK_PKCS1_OAEP_PADDING,0);
    if (len>0)
      break;
  }
  if (len<0) {
    log_info(LD_PROTOCOL,
             "Couldn't decrypt onionskin: client may be using old onion key");
    goto err;
  } else if (len != DH_KEY_LEN) {
    log_warn(LD_PROTOCOL, "Unexpected onionskin length after decryption: %ld",
             (long)len);
    goto err;
  }

  dh = crypto_dh_new(DH_TYPE_CIRCUIT);
  if (!dh) {
    log_warn(LD_BUG, "Couldn't allocate DH key");
    goto err;
  }
  if (crypto_dh_get_public(dh, handshake_reply_out, DH_KEY_LEN)) {
    log_info(LD_GENERAL, "crypto_dh_get_public failed.");
    goto err;
  }

  key_material_len = DIGEST_LEN+key_out_len;
  key_material = tor_malloc(key_material_len);
  len = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, dh, challenge,
                                 DH_KEY_LEN, key_material,
                                 key_material_len);
  if (len < 0) {
    log_info(LD_GENERAL, "crypto_dh_compute_secret failed.");
    goto err;
  }

  /* send back H(K|0) as proof that we learned K. */
  memcpy(handshake_reply_out+DH_KEY_LEN, key_material, DIGEST_LEN);

  /* use the rest of the key material for our shared keys, digests, etc */
  memcpy(key_out, key_material+DIGEST_LEN, key_out_len);

  memwipe(challenge, 0, sizeof(challenge));
  memwipe(key_material, 0, key_material_len);
  tor_free(key_material);
  crypto_dh_free(dh);
  return 0;
 err:
  memwipe(challenge, 0, sizeof(challenge));
  if (key_material) {
    memwipe(key_material, 0, key_material_len);
    tor_free(key_material);
  }
  if (dh) crypto_dh_free(dh);

  return -1;
}

/** Finish the client side of the DH handshake.
 * Given the 128 byte DH reply + 20 byte hash as generated by
 * onion_skin_server_handshake and the handshake state generated by
 * onion_skin_create, verify H(K) with the first 20 bytes of shared
 * key material, then generate key_out_len more bytes of shared key
 * material and store them in key_out.
 *
 * After the invocation, call crypto_dh_free on handshake_state.
 */
int
onion_skin_client_handshake(crypto_dh_t *handshake_state,
            const char *handshake_reply, /* ONIONSKIN_REPLY_LEN bytes */
            char *key_out,
            size_t key_out_len)
{
  ssize_t len;
  char *key_material=NULL;
  size_t key_material_len;
  tor_assert(crypto_dh_get_bytes(handshake_state) == DH_KEY_LEN);

  key_material_len = DIGEST_LEN + key_out_len;
  key_material = tor_malloc(key_material_len);
  len = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, handshake_state,
                                 handshake_reply, DH_KEY_LEN, key_material,
                                 key_material_len);
  if (len < 0)
    goto err;

  if (tor_memneq(key_material, handshake_reply+DH_KEY_LEN, DIGEST_LEN)) {
    /* H(K) does *not* match. Something fishy. */
    log_warn(LD_PROTOCOL,"Digest DOES NOT MATCH on onion handshake. "
             "Bug or attack.");
    goto err;
  }

  /* use the rest of the key material for our shared keys, digests, etc */
  memcpy(key_out, key_material+DIGEST_LEN, key_out_len);

  memwipe(key_material, 0, key_material_len);
  tor_free(key_material);
  return 0;
 err:
  memwipe(key_material, 0, key_material_len);
  tor_free(key_material);
  return -1;
}

/** Implement the server side of the CREATE_FAST abbreviated handshake.  The
 * client has provided DIGEST_LEN key bytes in <b>key_in</b> ("x").  We
 * generate a reply of DIGEST_LEN*2 bytes in <b>key_out</b>, consisting of a
 * new random "y", followed by H(x|y) to check for correctness.  We set
 * <b>key_out_len</b> bytes of key material in <b>key_out</b>.
 * Return 0 on success, &lt;0 on failure.
 **/
int
fast_server_handshake(const uint8_t *key_in, /* DIGEST_LEN bytes */
                      uint8_t *handshake_reply_out, /* DIGEST_LEN*2 bytes */
                      uint8_t *key_out,
                      size_t key_out_len)
{
  uint8_t tmp[DIGEST_LEN+DIGEST_LEN];
  uint8_t *out = NULL;
  size_t out_len;
  int r = -1;

  if (crypto_rand((char*)handshake_reply_out, DIGEST_LEN)<0)
    return -1;

  memcpy(tmp, key_in, DIGEST_LEN);
  memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN);
  out_len = key_out_len+DIGEST_LEN;
  out = tor_malloc(out_len);
  if (crypto_expand_key_material_TAP(tmp, sizeof(tmp), out, out_len)) {
    goto done;
  }
  memcpy(handshake_reply_out+DIGEST_LEN, out, DIGEST_LEN);
  memcpy(key_out, out+DIGEST_LEN, key_out_len);
  r = 0;
 done:
  memwipe(tmp, 0, sizeof(tmp));
  memwipe(out, 0, out_len);
  tor_free(out);
  return r;
}

/** Implement the second half of the client side of the CREATE_FAST handshake.
 * We sent the server <b>handshake_state</b> ("x") already, and the server
 * told us <b>handshake_reply_out</b> (y|H(x|y)).  Make sure that the hash is
 * correct, and generate key material in <b>key_out</b>.  Return 0 on success,
 * true on failure.
 *
 * NOTE: The "CREATE_FAST" handshake path is distinguishable from regular
 * "onionskin" handshakes, and is not secure if an adversary can see or modify
 * the messages.  Therefore, it should only be used by clients, and only as
 * the first hop of a circuit (since the first hop is already authenticated
 * and protected by TLS).
 */
int
fast_client_handshake(const uint8_t *handshake_state,/*DIGEST_LEN bytes*/
                      const uint8_t *handshake_reply_out,/*DIGEST_LEN*2 bytes*/
                      uint8_t *key_out,
                      size_t key_out_len)
{
  uint8_t tmp[DIGEST_LEN+DIGEST_LEN];
  uint8_t *out;
  size_t out_len;
  int r = -1;

  memcpy(tmp, handshake_state, DIGEST_LEN);
  memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN);
  out_len = key_out_len+DIGEST_LEN;
  out = tor_malloc(out_len);
  if (crypto_expand_key_material_TAP(tmp, sizeof(tmp), out, out_len)) {
    goto done;
  }
  if (tor_memneq(out, handshake_reply_out+DIGEST_LEN, DIGEST_LEN)) {
    /* H(K) does *not* match. Something fishy. */
    log_warn(LD_PROTOCOL,"Digest DOES NOT MATCH on fast handshake. "
             "Bug or attack.");
    goto done;
  }
  memcpy(key_out, out+DIGEST_LEN, key_out_len);
  r = 0;
 done:
  memwipe(tmp, 0, sizeof(tmp));
  memwipe(out, 0, out_len);
  tor_free(out);
  return r;
}

/** Remove all circuits from the pending list.  Called from tor_free_all. */
void
clear_pending_onions(void)
Loading