[tor-commits] [tor/master] Split onion.[ch] into onion{, _fast, _tap}.[ch]

nickm at torproject.org nickm at torproject.org
Thu Jan 3 16:52:57 UTC 2013


commit f7e590df05b1b3568a68ee3eae3965cb58e13de7
Author: Nick Mathewson <nickm at torproject.org>
Date:   Tue Dec 4 16:09:52 2012 -0500

    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.
---
 src/or/circuitbuild.c |    3 +-
 src/or/command.c      |    3 +-
 src/or/cpuworker.c    |    1 +
 src/or/include.am     |    4 +
 src/or/onion.c        |  275 -------------------------------------------------
 src/or/onion.h        |   27 -----
 src/or/onion_fast.c   |   97 +++++++++++++++++
 src/or/onion_fast.h   |   26 +++++
 src/or/onion_tap.c    |  214 ++++++++++++++++++++++++++++++++++++++
 src/or/onion_tap.h    |   32 ++++++
 src/test/bench.c      |    2 +-
 src/test/test.c       |    2 +-
 12 files changed, 380 insertions(+), 306 deletions(-)

diff --git a/src/or/circuitbuild.c b/src/or/circuitbuild.c
index 1fb93bb..33a1351 100644
--- a/src/or/circuitbuild.c
+++ b/src/or/circuitbuild.c
@@ -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"
diff --git a/src/or/command.c b/src/or/command.c
index 39eccdf..2718ec9 100644
--- a/src/or/command.c
+++ b/src/or/command.c
@@ -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"
diff --git a/src/or/cpuworker.c b/src/or/cpuworker.c
index 119892d..3a3d687 100644
--- a/src/or/cpuworker.c
+++ b/src/or/cpuworker.c
@@ -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. */
diff --git a/src/or/include.am b/src/or/include.am
index 1808849..600f9d5 100644
--- a/src/or/include.am
+++ b/src/or/include.am
@@ -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				\
diff --git a/src/or/onion.c b/src/or/onion.c
index 4720515..f468ada 100644
--- a/src/or/onion.c
+++ b/src/or/onion.c
@@ -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, <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)
diff --git a/src/or/onion.h b/src/or/onion.h
index e7626f9..55ea3f9 100644
--- a/src/or/onion.h
+++ b/src/or/onion.h
@@ -15,33 +15,6 @@
 int onion_pending_add(or_circuit_t *circ, char *onionskin);
 or_circuit_t *onion_next_task(char **onionskin_out);
 void onion_pending_remove(or_circuit_t *circ);
-
-int onion_skin_create(crypto_pk_t *router_key,
-                      crypto_dh_t **handshake_state_out,
-                      char *onion_skin_out);
-
-int onion_skin_server_handshake(const char *onion_skin,
-                                crypto_pk_t *private_key,
-                                crypto_pk_t *prev_private_key,
-                                char *handshake_reply_out,
-                                char *key_out,
-                                size_t key_out_len);
-
-int onion_skin_client_handshake(crypto_dh_t *handshake_state,
-                                const char *handshake_reply,
-                                char *key_out,
-                                size_t key_out_len);
-
-int fast_server_handshake(const uint8_t *key_in,
-                          uint8_t *handshake_reply_out,
-                          uint8_t *key_out,
-                          size_t key_out_len);
-
-int fast_client_handshake(const uint8_t *handshake_state,
-                          const uint8_t *handshake_reply_out,
-                          uint8_t *key_out,
-                          size_t key_out_len);
-
 void clear_pending_onions(void);
 
 #endif
diff --git a/src/or/onion_fast.c b/src/or/onion_fast.c
new file mode 100644
index 0000000..8d09de7
--- /dev/null
+++ b/src/or/onion_fast.c
@@ -0,0 +1,97 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2012, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file onion_fast.c
+ * \brief Functions implement the CREATE_FAST circuit handshake.
+ **/
+
+#include "or.h"
+#include "onion_fast.h"
+
+/** 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, <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;
+}
+
diff --git a/src/or/onion_fast.h b/src/or/onion_fast.h
new file mode 100644
index 0000000..5c9d59e
--- /dev/null
+++ b/src/or/onion_fast.h
@@ -0,0 +1,26 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2012, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file onion_fast.h
+ * \brief Header file for onion_fast.c.
+ **/
+
+#ifndef TOR_ONION_FAST_H
+#define TOR_ONION_FAST_H
+
+int fast_server_handshake(const uint8_t *key_in,
+                          uint8_t *handshake_reply_out,
+                          uint8_t *key_out,
+                          size_t key_out_len);
+
+int fast_client_handshake(const uint8_t *handshake_state,
+                          const uint8_t *handshake_reply_out,
+                          uint8_t *key_out,
+                          size_t key_out_len);
+
+#endif
+
diff --git a/src/or/onion_tap.c b/src/or/onion_tap.c
new file mode 100644
index 0000000..464b845
--- /dev/null
+++ b/src/or/onion_tap.c
@@ -0,0 +1,214 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2012, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file onion_tap.c
+ * \brief Functions to implement the original Tor circuit extension handshake
+ * (a.k.a TAP).
+ *
+ * We didn't call it "TAP" ourselves -- Ian Goldberg named it in "On the
+ * Security of the Tor Authentication Protocol".  (Spoiler: it's secure, but
+ * its security is kind of fragile and implementation dependent.  Never modify
+ * this implementation without reading and understanding that paper at least.)
+ **/
+
+#include "or.h"
+#include "config.h"
+#include "onion_tap.h"
+#include "rephist.h"
+
+/*----------------------------------------------------------------------*/
+
+/** 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;
+}
+
diff --git a/src/or/onion_tap.h b/src/or/onion_tap.h
new file mode 100644
index 0000000..3bd90c9
--- /dev/null
+++ b/src/or/onion_tap.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2001 Matej Pfajfar.
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2012, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file onion_tap.h
+ * \brief Header file for onion_tap.c.
+ **/
+
+#ifndef TOR_ONION_TAP_H
+#define TOR_ONION_TAP_H
+
+int onion_skin_create(crypto_pk_t *router_key,
+                      crypto_dh_t **handshake_state_out,
+                      char *onion_skin_out);
+
+int onion_skin_server_handshake(const char *onion_skin,
+                                crypto_pk_t *private_key,
+                                crypto_pk_t *prev_private_key,
+                                char *handshake_reply_out,
+                                char *key_out,
+                                size_t key_out_len);
+
+int onion_skin_client_handshake(crypto_dh_t *handshake_state,
+                                const char *handshake_reply,
+                                char *key_out,
+                                size_t key_out_len);
+
+#endif
+
diff --git a/src/test/bench.c b/src/test/bench.c
index de7e4e5..567b1a4 100644
--- a/src/test/bench.c
+++ b/src/test/bench.c
@@ -18,7 +18,7 @@ const char tor_git_revision[] = "";
 #define CONFIG_PRIVATE
 
 #include "or.h"
-#include "onion.h"
+#include "onion_tap.h"
 #include "relay.h"
 #include "config.h"
 #ifdef CURVE25519_ENABLED
diff --git a/src/test/test.c b/src/test/test.c
index 78f9c06..cc2d481 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -53,7 +53,7 @@ double fabs(double x);
 #include "torgzip.h"
 #include "mempool.h"
 #include "memarea.h"
-#include "onion.h"
+#include "onion_tap.h"
 #include "policies.h"
 #include "rephist.h"
 #include "routerparse.h"





More information about the tor-commits mailing list