Browse Source

r17963@catbus: nickm | 2008-02-07 10:14:25 -0500
Be more thorough about memory poisoning and clearing. Add an in-place version of aes_crypt in order to remove a memcpy from relay_crypt_one_payload.


svn:r13414

Nick Mathewson 16 years ago
parent
commit
eecc44dab8
9 changed files with 117 additions and 33 deletions
  1. 6 0
      ChangeLog
  2. 37 1
      src/common/aes.c
  3. 1 0
      src/common/aes.h
  4. 23 2
      src/common/crypto.c
  5. 1 0
      src/common/crypto.h
  6. 5 2
      src/or/circuitlist.c
  7. 8 1
      src/or/connection.c
  8. 34 20
      src/or/onion.c
  9. 2 7
      src/or/relay.c

+ 6 - 0
ChangeLog

@@ -13,6 +13,10 @@ Changes in version 0.2.0.19-alpha - 2008-02-??
     - Give more descriptive well-formedness errors for out-of-range
     - Give more descriptive well-formedness errors for out-of-range
       hidden service descriptor/protocol versions.
       hidden service descriptor/protocol versions.
 
 
+  o Minor features (security):
+    - Be slightly more paranoid about overwriting sensitive memory on free,
+      as a defensive programming tactic to ensure forward secrecy.
+
   o Deprecated features (controller):
   o Deprecated features (controller):
     - The status/version/num-versioning and status/version/num-concurring
     - The status/version/num-versioning and status/version/num-concurring
       GETINFO options are no longer useful in the V3 directory protocol:
       GETINFO options are no longer useful in the V3 directory protocol:
@@ -59,6 +63,8 @@ Changes in version 0.2.0.19-alpha - 2008-02-??
       from a CREATE cell that we are waiting for a cpuworker to be
       from a CREATE cell that we are waiting for a cpuworker to be
       assigned" and "onionskin from an EXTEND cell that we are going to
       assigned" and "onionskin from an EXTEND cell that we are going to
       send to an OR as soon as we are connected".
       send to an OR as soon as we are connected".
+    - Add an in-place version of aes_crypt so that we can avoid doing a
+      needless memcpy() call on each cell payload.
 
 
 
 
 Changes in version 0.2.0.18-alpha - 2008-01-25
 Changes in version 0.2.0.18-alpha - 2008-01-25

+ 37 - 1
src/common/aes.c

@@ -289,7 +289,7 @@ aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len,
    * of alignmement, using a bigger buffer, and so on. Not till after 0.1.2.x,
    * of alignmement, using a bigger buffer, and so on. Not till after 0.1.2.x,
    * though. */
    * though. */
   int c = cipher->pos;
   int c = cipher->pos;
-  if (!len) return;
+  if (PREDICT_UNLIKELY(!len)) return;
 
 
   while (1) {
   while (1) {
     do {
     do {
@@ -312,6 +312,42 @@ aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len,
   }
   }
 }
 }
 
 
+/** Encrypt <b>len</b> bytes from <b>input</b>, storing the results in place.
+ * Uses the key in <b>cipher</b>, and advances the counter by <b>len</b> bytes
+ * as it encrypts.
+ */
+void
+aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len)
+{
+
+  /* XXXX This function is up to 5% of our runtime in some profiles;
+   * we should look into unrolling some of the loops; taking advantage
+   * of alignmement, using a bigger buffer, and so on. Not till after 0.1.2.x,
+   * though. */
+  int c = cipher->pos;
+  if (PREDICT_UNLIKELY(!len)) return;
+
+  while (1) {
+    do {
+      if (len-- == 0) { cipher->pos = c; return; }
+      *(data++) ^= cipher->buf[c];
+    } while (++c != 16);
+    cipher->pos = c = 0;
+    if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 0))) {
+      if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 1))) {
+        if (PREDICT_UNLIKELY(! ++COUNTER(cipher, 2))) {
+          ++COUNTER(cipher, 3);
+          UPDATE_CTR_BUF(cipher, 3);
+        }
+        UPDATE_CTR_BUF(cipher, 2);
+      }
+      UPDATE_CTR_BUF(cipher, 1);
+    }
+    UPDATE_CTR_BUF(cipher, 0);
+    _aes_fill_buf(cipher);
+  }
+}
+
 /** Reset the 128-bit counter of <b>cipher</b> to the 16-bit big-endian value
 /** Reset the 128-bit counter of <b>cipher</b> to the 16-bit big-endian value
  * in <b>iv</b>. */
  * in <b>iv</b>. */
 void
 void

+ 1 - 0
src/common/aes.h

@@ -25,6 +25,7 @@ void aes_free_cipher(aes_cnt_cipher_t *cipher);
 void aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits);
 void aes_set_key(aes_cnt_cipher_t *cipher, const char *key, int key_bits);
 void aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len,
 void aes_crypt(aes_cnt_cipher_t *cipher, const char *input, size_t len,
                char *output);
                char *output);
+void aes_crypt_inplace(aes_cnt_cipher_t *cipher, char *data, size_t len);
 void aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv);
 void aes_set_iv(aes_cnt_cipher_t *cipher, const char *iv);
 
 
 #endif
 #endif

+ 23 - 2
src/common/crypto.c

@@ -380,6 +380,7 @@ crypto_free_cipher_env(crypto_cipher_env_t *env)
 
 
   tor_assert(env->cipher);
   tor_assert(env->cipher);
   aes_free_cipher(env->cipher);
   aes_free_cipher(env->cipher);
+  memset(env, 0, sizeof(crypto_cipher_env_t));
   tor_free(env);
   tor_free(env);
 }
 }
 
 
@@ -775,10 +776,13 @@ int
 crypto_pk_private_sign_digest(crypto_pk_env_t *env, char *to,
 crypto_pk_private_sign_digest(crypto_pk_env_t *env, char *to,
                               const char *from, size_t fromlen)
                               const char *from, size_t fromlen)
 {
 {
+  int r;
   char digest[DIGEST_LEN];
   char digest[DIGEST_LEN];
   if (crypto_digest(digest,from,fromlen)<0)
   if (crypto_digest(digest,from,fromlen)<0)
     return -1;
     return -1;
-  return crypto_pk_private_sign(env,to,digest,DIGEST_LEN);
+  r = crypto_pk_private_sign(env,to,digest,DIGEST_LEN);
+  memset(digest, 0, sizeof(digest));
+  return r;
 }
 }
 
 
 /** Perform a hybrid (public/secret) encryption on <b>fromlen</b>
 /** Perform a hybrid (public/secret) encryption on <b>fromlen</b>
@@ -1156,6 +1160,16 @@ crypto_cipher_decrypt(crypto_cipher_env_t *env, char *to,
   return 0;
   return 0;
 }
 }
 
 
+/** Encrypt <b>len</b> bytes on <b>from</b> using the cipher in <b>env</b>;
+ * on success, return 0.  On failure, return -1.
+ */
+int
+crypto_cipher_crypt_inplace(crypto_cipher_env_t *env, char *buf, size_t len)
+{
+  aes_crypt_inplace(env->cipher, buf, len);
+  return 0;
+}
+
 /** Encrypt <b>fromlen</b> bytes (at least 1) from <b>from</b> with the key in
 /** Encrypt <b>fromlen</b> bytes (at least 1) from <b>from</b> with the key in
  * <b>cipher</b> to the buffer in <b>to</b> of length
  * <b>cipher</b> to the buffer in <b>to</b> of length
  * <b>tolen</b>. <b>tolen</b> must be at least <b>fromlen</b> plus
  * <b>tolen</b>. <b>tolen</b> must be at least <b>fromlen</b> plus
@@ -1250,6 +1264,7 @@ crypto_new_digest_env(void)
 void
 void
 crypto_free_digest_env(crypto_digest_env_t *digest)
 crypto_free_digest_env(crypto_digest_env_t *digest)
 {
 {
+  memset(digest, 0, sizeof(crypto_digest_env_t));
   tor_free(digest);
   tor_free(digest);
 }
 }
 
 
@@ -1286,6 +1301,7 @@ crypto_digest_get_digest(crypto_digest_env_t *digest,
   memcpy(&tmpctx, &digest->d, sizeof(SHA_CTX));
   memcpy(&tmpctx, &digest->d, sizeof(SHA_CTX));
   SHA1_Final(r, &tmpctx);
   SHA1_Final(r, &tmpctx);
   memcpy(out, r, out_len);
   memcpy(out, r, out_len);
+  memset(r, 0, sizeof(r));
 }
 }
 
 
 /** Allocate and return a new digest object with the same state as
 /** Allocate and return a new digest object with the same state as
@@ -1588,11 +1604,13 @@ crypto_expand_key_material(const char *key_in, size_t key_in_len,
   }
   }
   memset(tmp, 0, key_in_len+1);
   memset(tmp, 0, key_in_len+1);
   tor_free(tmp);
   tor_free(tmp);
+  memset(digest, 0, sizeof(digest));
   return 0;
   return 0;
 
 
  err:
  err:
   memset(tmp, 0, key_in_len+1);
   memset(tmp, 0, key_in_len+1);
   tor_free(tmp);
   tor_free(tmp);
+  memset(digest, 0, sizeof(digest));
   return -1;
   return -1;
 }
 }
 
 
@@ -1668,6 +1686,7 @@ crypto_seed_rng(void)
     return rand_poll_status ? 0 : -1;
     return rand_poll_status ? 0 : -1;
   }
   }
   RAND_seed(buf, sizeof(buf));
   RAND_seed(buf, sizeof(buf));
+  memset(buf, 0, sizeof(buf));
   return 0;
   return 0;
 #else
 #else
   for (i = 0; filenames[i]; ++i) {
   for (i = 0; filenames[i]; ++i) {
@@ -1682,6 +1701,7 @@ crypto_seed_rng(void)
       return -1;
       return -1;
     }
     }
     RAND_seed(buf, sizeof(buf));
     RAND_seed(buf, sizeof(buf));
+    memset(buf, 0, sizeof(buf));
     return 0;
     return 0;
   }
   }
 
 
@@ -1863,7 +1883,6 @@ base64_decode(char *dest, size_t destlen, const char *src, size_t srclen)
   ret += len;
   ret += len;
   return ret;
   return ret;
 #else
 #else
-  #define ACC32
   const char *eos = src+srclen;
   const char *eos = src+srclen;
   uint32_t n=0;
   uint32_t n=0;
   int n_idx=0;
   int n_idx=0;
@@ -2056,6 +2075,7 @@ base32_decode(char *dest, size_t destlen, const char *src, size_t srclen)
     }
     }
   }
   }
 
 
+  memset(tmp, 0, srclen);
   tor_free(tmp);
   tor_free(tmp);
   tmp = NULL;
   tmp = NULL;
   return 0;
   return 0;
@@ -2099,6 +2119,7 @@ secret_to_key(char *key_out, size_t key_out_len, const char *secret,
     }
     }
   }
   }
   crypto_digest_get_digest(d, key_out, key_out_len);
   crypto_digest_get_digest(d, key_out, key_out_len);
+  memset(tmp, 0, 8+secret_len);
   tor_free(tmp);
   tor_free(tmp);
   crypto_free_digest_env(d);
   crypto_free_digest_env(d);
 }
 }

+ 1 - 0
src/common/crypto.h

@@ -128,6 +128,7 @@ int crypto_cipher_encrypt(crypto_cipher_env_t *env, char *to,
                           const char *from, size_t fromlen);
                           const char *from, size_t fromlen);
 int crypto_cipher_decrypt(crypto_cipher_env_t *env, char *to,
 int crypto_cipher_decrypt(crypto_cipher_env_t *env, char *to,
                           const char *from, size_t fromlen);
                           const char *from, size_t fromlen);
+int crypto_cipher_crypt_inplace(crypto_cipher_env_t *env, char *d, size_t len);
 
 
 int crypto_cipher_encrypt_with_iv(crypto_cipher_env_t *env,
 int crypto_cipher_encrypt_with_iv(crypto_cipher_env_t *env,
                                   char *to, size_t tolen,
                                   char *to, size_t tolen,

+ 5 - 2
src/or/circuitlist.c

@@ -378,10 +378,12 @@ static void
 circuit_free(circuit_t *circ)
 circuit_free(circuit_t *circ)
 {
 {
   void *mem;
   void *mem;
+  size_t memlen;
   tor_assert(circ);
   tor_assert(circ);
   if (CIRCUIT_IS_ORIGIN(circ)) {
   if (CIRCUIT_IS_ORIGIN(circ)) {
     origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
     origin_circuit_t *ocirc = TO_ORIGIN_CIRCUIT(circ);
     mem = ocirc;
     mem = ocirc;
+    memlen = sizeof(origin_circuit_t);
     tor_assert(circ->magic == ORIGIN_CIRCUIT_MAGIC);
     tor_assert(circ->magic == ORIGIN_CIRCUIT_MAGIC);
     if (ocirc->build_state) {
     if (ocirc->build_state) {
       if (ocirc->build_state->chosen_exit)
       if (ocirc->build_state->chosen_exit)
@@ -398,6 +400,7 @@ circuit_free(circuit_t *circ)
   } else {
   } else {
     or_circuit_t *ocirc = TO_OR_CIRCUIT(circ);
     or_circuit_t *ocirc = TO_OR_CIRCUIT(circ);
     mem = ocirc;
     mem = ocirc;
+    memlen = sizeof(or_circuit_t);
     tor_assert(circ->magic == OR_CIRCUIT_MAGIC);
     tor_assert(circ->magic == OR_CIRCUIT_MAGIC);
 
 
     if (ocirc->p_crypto)
     if (ocirc->p_crypto)
@@ -432,7 +435,7 @@ circuit_free(circuit_t *circ)
    * "active" checks will be violated. */
    * "active" checks will be violated. */
   cell_queue_clear(&circ->n_conn_cells);
   cell_queue_clear(&circ->n_conn_cells);
 
 
-  memset(circ, 0xAA, sizeof(circuit_t)); /* poison memory */
+  memset(circ, 0xAA, memlen); /* poison memory */
   tor_free(mem);
   tor_free(mem);
 }
 }
 
 
@@ -499,7 +502,7 @@ circuit_free_cpath_node(crypt_path_t *victim)
   if (victim->extend_info)
   if (victim->extend_info)
     extend_info_free(victim->extend_info);
     extend_info_free(victim->extend_info);
 
 
-  victim->magic = 0xDEADBEEFu;
+  memset(victim, 0xBB, sizeof(crypt_path_t)); /* poison memory */
   tor_free(victim);
   tor_free(victim);
 }
 }
 
 

+ 8 - 1
src/or/connection.c

@@ -266,27 +266,33 @@ static void
 _connection_free(connection_t *conn)
 _connection_free(connection_t *conn)
 {
 {
   void *mem;
   void *mem;
+  size_t memlen;
   switch (conn->type) {
   switch (conn->type) {
     case CONN_TYPE_OR:
     case CONN_TYPE_OR:
       tor_assert(conn->magic == OR_CONNECTION_MAGIC);
       tor_assert(conn->magic == OR_CONNECTION_MAGIC);
       mem = TO_OR_CONN(conn);
       mem = TO_OR_CONN(conn);
+      memlen = sizeof(or_connection_t);
       break;
       break;
     case CONN_TYPE_AP:
     case CONN_TYPE_AP:
     case CONN_TYPE_EXIT:
     case CONN_TYPE_EXIT:
       tor_assert(conn->magic == EDGE_CONNECTION_MAGIC);
       tor_assert(conn->magic == EDGE_CONNECTION_MAGIC);
       mem = TO_EDGE_CONN(conn);
       mem = TO_EDGE_CONN(conn);
+      memlen = sizeof(edge_connection_t);
       break;
       break;
     case CONN_TYPE_DIR:
     case CONN_TYPE_DIR:
       tor_assert(conn->magic == DIR_CONNECTION_MAGIC);
       tor_assert(conn->magic == DIR_CONNECTION_MAGIC);
       mem = TO_DIR_CONN(conn);
       mem = TO_DIR_CONN(conn);
+      memlen = sizeof(dir_connection_t);
       break;
       break;
     case CONN_TYPE_CONTROL:
     case CONN_TYPE_CONTROL:
       tor_assert(conn->magic == CONTROL_CONNECTION_MAGIC);
       tor_assert(conn->magic == CONTROL_CONNECTION_MAGIC);
       mem = TO_CONTROL_CONN(conn);
       mem = TO_CONTROL_CONN(conn);
+      memlen = sizeof(control_connection_t);
       break;
       break;
     default:
     default:
       tor_assert(conn->magic == BASE_CONNECTION_MAGIC);
       tor_assert(conn->magic == BASE_CONNECTION_MAGIC);
       mem = conn;
       mem = conn;
+      memlen = sizeof(connection_t);
       break;
       break;
   }
   }
 
 
@@ -331,6 +337,7 @@ _connection_free(connection_t *conn)
   if (CONN_IS_EDGE(conn)) {
   if (CONN_IS_EDGE(conn)) {
     edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
     edge_connection_t *edge_conn = TO_EDGE_CONN(conn);
     tor_free(edge_conn->chosen_exit_name);
     tor_free(edge_conn->chosen_exit_name);
+    memset(edge_conn->socks_request, 0xcc, sizeof(socks_request_t));
     tor_free(edge_conn->socks_request);
     tor_free(edge_conn->socks_request);
   }
   }
   if (conn->type == CONN_TYPE_CONTROL) {
   if (conn->type == CONN_TYPE_CONTROL) {
@@ -365,7 +372,7 @@ _connection_free(connection_t *conn)
     connection_or_remove_from_identity_map(TO_OR_CONN(conn));
     connection_or_remove_from_identity_map(TO_OR_CONN(conn));
   }
   }
 
 
-  memset(conn, 0xAA, sizeof(connection_t)); /* poison memory */
+  memset(conn, 0xAA, memlen); /* poison memory */
   tor_free(mem);
   tor_free(mem);
 }
 }
 
 

+ 34 - 20
src/or/onion.c

@@ -166,7 +166,7 @@ onion_skin_create(crypto_pk_env_t *dest_router_key,
                   crypto_dh_env_t **handshake_state_out,
                   crypto_dh_env_t **handshake_state_out,
                   char *onion_skin_out) /* ONIONSKIN_CHALLENGE_LEN bytes */
                   char *onion_skin_out) /* ONIONSKIN_CHALLENGE_LEN bytes */
 {
 {
-  char *challenge = NULL;
+  char challenge[DH_KEY_LEN];
   crypto_dh_env_t *dh = NULL;
   crypto_dh_env_t *dh = NULL;
   int dhbytes, pkbytes;
   int dhbytes, pkbytes;
 
 
@@ -183,7 +183,6 @@ onion_skin_create(crypto_pk_env_t *dest_router_key,
   pkbytes = crypto_pk_keysize(dest_router_key);
   pkbytes = crypto_pk_keysize(dest_router_key);
   tor_assert(dhbytes == 128);
   tor_assert(dhbytes == 128);
   tor_assert(pkbytes == 128);
   tor_assert(pkbytes == 128);
-  challenge = tor_malloc_zero(DH_KEY_LEN);
 
 
   if (crypto_dh_get_public(dh, challenge, dhbytes))
   if (crypto_dh_get_public(dh, challenge, dhbytes))
     goto err;
     goto err;
@@ -211,12 +210,12 @@ onion_skin_create(crypto_pk_env_t *dest_router_key,
                                       PK_PKCS1_OAEP_PADDING, 1)<0)
                                       PK_PKCS1_OAEP_PADDING, 1)<0)
     goto err;
     goto err;
 
 
-  tor_free(challenge);
+  memset(challenge, 0, sizeof(challenge));
   *handshake_state_out = dh;
   *handshake_state_out = dh;
 
 
   return 0;
   return 0;
  err:
  err:
-  tor_free(challenge);
+  memset(challenge, 0, sizeof(challenge));
   if (dh) crypto_dh_free(dh);
   if (dh) crypto_dh_free(dh);
   return -1;
   return -1;
 }
 }
@@ -238,6 +237,7 @@ onion_skin_server_handshake(const char *onion_skin, /*ONIONSKIN_CHALLENGE_LEN*/
   crypto_dh_env_t *dh = NULL;
   crypto_dh_env_t *dh = NULL;
   int len;
   int len;
   char *key_material=NULL;
   char *key_material=NULL;
+  size_t key_material_len=0;
   int i;
   int i;
   crypto_pk_env_t *k;
   crypto_pk_env_t *k;
 
 
@@ -277,9 +277,10 @@ onion_skin_server_handshake(const char *onion_skin, /*ONIONSKIN_CHALLENGE_LEN*/
   puts("");
   puts("");
 #endif
 #endif
 
 
-  key_material = tor_malloc(DIGEST_LEN+key_out_len);
+  key_material_len = DIGEST_LEN+key_out_len;
+  key_material = tor_malloc(key_material_len);
   len = crypto_dh_compute_secret(dh, challenge, DH_KEY_LEN,
   len = crypto_dh_compute_secret(dh, challenge, DH_KEY_LEN,
-                                 key_material, DIGEST_LEN+key_out_len);
+                                 key_material, key_material_len);
   if (len < 0) {
   if (len < 0) {
     log_info(LD_GENERAL, "crypto_dh_compute_secret failed.");
     log_info(LD_GENERAL, "crypto_dh_compute_secret failed.");
     goto err;
     goto err;
@@ -300,11 +301,17 @@ onion_skin_server_handshake(const char *onion_skin, /*ONIONSKIN_CHALLENGE_LEN*/
   puts("");
   puts("");
 #endif
 #endif
 
 
+  memset(challenge, 0, sizeof(challenge));
+  memset(key_material, 0, key_material_len);
   tor_free(key_material);
   tor_free(key_material);
   crypto_dh_free(dh);
   crypto_dh_free(dh);
   return 0;
   return 0;
  err:
  err:
-  tor_free(key_material);
+  memset(challenge, 0, sizeof(challenge));
+  if (key_material) {
+    memset(key_material, 0, key_material_len);
+    tor_free(key_material);
+  }
   if (dh) crypto_dh_free(dh);
   if (dh) crypto_dh_free(dh);
 
 
   return -1;
   return -1;
@@ -327,6 +334,7 @@ onion_skin_client_handshake(crypto_dh_env_t *handshake_state,
 {
 {
   int len;
   int len;
   char *key_material=NULL;
   char *key_material=NULL;
+  size_t key_material_len;
   tor_assert(crypto_dh_get_bytes(handshake_state) == DH_KEY_LEN);
   tor_assert(crypto_dh_get_bytes(handshake_state) == DH_KEY_LEN);
 
 
 #ifdef DEBUG_ONION_SKINS
 #ifdef DEBUG_ONION_SKINS
@@ -337,13 +345,14 @@ onion_skin_client_handshake(crypto_dh_env_t *handshake_state,
   puts("");
   puts("");
 #endif
 #endif
 
 
-  key_material = tor_malloc(20+key_out_len);
+  key_material_len = DIGEST_LEN + key_out_len;
+  key_material = tor_malloc(key_material_len);
   len = crypto_dh_compute_secret(handshake_state, handshake_reply, DH_KEY_LEN,
   len = crypto_dh_compute_secret(handshake_state, handshake_reply, DH_KEY_LEN,
-                                 key_material, 20+key_out_len);
+                                 key_material, key_material_len);
   if (len < 0)
   if (len < 0)
     goto err;
     goto err;
 
 
-  if (memcmp(key_material, handshake_reply+DH_KEY_LEN, 20)) {
+  if (memcmp(key_material, handshake_reply+DH_KEY_LEN, DIGEST_LEN)) {
     /* H(K) does *not* match. Something fishy. */
     /* H(K) does *not* match. Something fishy. */
     log_warn(LD_PROTOCOL,"Digest DOES NOT MATCH on onion handshake. "
     log_warn(LD_PROTOCOL,"Digest DOES NOT MATCH on onion handshake. "
              "Bug or attack.");
              "Bug or attack.");
@@ -351,7 +360,7 @@ onion_skin_client_handshake(crypto_dh_env_t *handshake_state,
   }
   }
 
 
   /* use the rest of the key material for our shared keys, digests, etc */
   /* use the rest of the key material for our shared keys, digests, etc */
-  memcpy(key_out, key_material+20, key_out_len);
+  memcpy(key_out, key_material+DIGEST_LEN, key_out_len);
 
 
 #ifdef DEBUG_ONION_SKINS
 #ifdef DEBUG_ONION_SKINS
   printf("Client: keys out:");
   printf("Client: keys out:");
@@ -359,9 +368,11 @@ onion_skin_client_handshake(crypto_dh_env_t *handshake_state,
   puts("");
   puts("");
 #endif
 #endif
 
 
+  memset(key_material, 0, key_material_len);
   tor_free(key_material);
   tor_free(key_material);
   return 0;
   return 0;
  err:
  err:
+  memset(key_material, 0, key_material_len);
   tor_free(key_material);
   tor_free(key_material);
   return -1;
   return -1;
 }
 }
@@ -380,8 +391,9 @@ fast_server_handshake(const char *key_in, /* DIGEST_LEN bytes */
                       size_t key_out_len)
                       size_t key_out_len)
 {
 {
   char tmp[DIGEST_LEN+DIGEST_LEN];
   char tmp[DIGEST_LEN+DIGEST_LEN];
-  char *out;
+  char *out = NULL;
   size_t out_len;
   size_t out_len;
+  int r = -1;
 
 
   if (crypto_rand(handshake_reply_out, DIGEST_LEN)<0)
   if (crypto_rand(handshake_reply_out, DIGEST_LEN)<0)
     return -1;
     return -1;
@@ -391,15 +403,16 @@ fast_server_handshake(const char *key_in, /* DIGEST_LEN bytes */
   out_len = key_out_len+DIGEST_LEN;
   out_len = key_out_len+DIGEST_LEN;
   out = tor_malloc(out_len);
   out = tor_malloc(out_len);
   if (crypto_expand_key_material(tmp, sizeof(tmp), out, out_len)) {
   if (crypto_expand_key_material(tmp, sizeof(tmp), out, out_len)) {
-    tor_free(out);
-    return -1;
+    goto done;
   }
   }
   memcpy(handshake_reply_out+DIGEST_LEN, out, DIGEST_LEN);
   memcpy(handshake_reply_out+DIGEST_LEN, out, DIGEST_LEN);
   memcpy(key_out, out+DIGEST_LEN, key_out_len);
   memcpy(key_out, out+DIGEST_LEN, key_out_len);
+  r = 0;
+ done:
   memset(tmp, 0, sizeof(tmp));
   memset(tmp, 0, sizeof(tmp));
   memset(out, 0, out_len);
   memset(out, 0, out_len);
   tor_free(out);
   tor_free(out);
-  return 0;
+  return r;
 }
 }
 
 
 /** Implement the second half of the client side of the CREATE_FAST handshake.
 /** Implement the second half of the client side of the CREATE_FAST handshake.
@@ -423,27 +436,28 @@ fast_client_handshake(const char *handshake_state, /* DIGEST_LEN bytes */
   char tmp[DIGEST_LEN+DIGEST_LEN];
   char tmp[DIGEST_LEN+DIGEST_LEN];
   char *out;
   char *out;
   size_t out_len;
   size_t out_len;
+  int r;
 
 
   memcpy(tmp, handshake_state, DIGEST_LEN);
   memcpy(tmp, handshake_state, DIGEST_LEN);
   memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN);
   memcpy(tmp+DIGEST_LEN, handshake_reply_out, DIGEST_LEN);
   out_len = key_out_len+DIGEST_LEN;
   out_len = key_out_len+DIGEST_LEN;
   out = tor_malloc(out_len);
   out = tor_malloc(out_len);
   if (crypto_expand_key_material(tmp, sizeof(tmp), out, out_len)) {
   if (crypto_expand_key_material(tmp, sizeof(tmp), out, out_len)) {
-    tor_free(out);
-    return -1;
+    goto done;
   }
   }
   if (memcmp(out, handshake_reply_out+DIGEST_LEN, DIGEST_LEN)) {
   if (memcmp(out, handshake_reply_out+DIGEST_LEN, DIGEST_LEN)) {
     /* H(K) does *not* match. Something fishy. */
     /* H(K) does *not* match. Something fishy. */
     log_warn(LD_PROTOCOL,"Digest DOES NOT MATCH on fast handshake. "
     log_warn(LD_PROTOCOL,"Digest DOES NOT MATCH on fast handshake. "
              "Bug or attack.");
              "Bug or attack.");
-    tor_free(out);
-    return -1;
+    goto done;
   }
   }
   memcpy(key_out, out+DIGEST_LEN, key_out_len);
   memcpy(key_out, out+DIGEST_LEN, key_out_len);
+  r = 0;
+ done:
   memset(tmp, 0, sizeof(tmp));
   memset(tmp, 0, sizeof(tmp));
   memset(out, 0, out_len);
   memset(out, 0, out_len);
   tor_free(out);
   tor_free(out);
-  return 0;
+  return r;
 }
 }
 
 
 /** Remove all circuits from the pending list.  Called from tor_free_all. */
 /** Remove all circuits from the pending list.  Called from tor_free_all. */

+ 2 - 7
src/or/relay.c

@@ -117,19 +117,14 @@ static int
 relay_crypt_one_payload(crypto_cipher_env_t *cipher, char *in,
 relay_crypt_one_payload(crypto_cipher_env_t *cipher, char *in,
                         int encrypt_mode)
                         int encrypt_mode)
 {
 {
-  char out[CELL_PAYLOAD_SIZE]; /* 'in' must be this size too */
   int r;
   int r;
-
-  if (encrypt_mode)
-    r = crypto_cipher_encrypt(cipher, out, in, CELL_PAYLOAD_SIZE);
-  else
-    r = crypto_cipher_decrypt(cipher, out, in, CELL_PAYLOAD_SIZE);
+  (void)encrypt_mode;
+  r = crypto_cipher_crypt_inplace(cipher, in, CELL_PAYLOAD_SIZE);
 
 
   if (r) {
   if (r) {
     log_warn(LD_BUG,"Error during relay encryption");
     log_warn(LD_BUG,"Error during relay encryption");
     return -1;
     return -1;
   }
   }
-  memcpy(in,out,CELL_PAYLOAD_SIZE);
   return 0;
   return 0;
 }
 }