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

          Line data    Source code
       1             : /* Copyright (c) 2001 Matej Pfajfar.
       2             :  * Copyright (c) 2001-2004, Roger Dingledine.
       3             :  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
       4             :  * Copyright (c) 2007-2021, The Tor Project, Inc. */
       5             : /* See LICENSE for licensing information */
       6             : 
       7             : /**
       8             :  * \file onion_tap.c
       9             :  * \brief Functions to implement the original Tor circuit extension handshake
      10             :  * (a.k.a TAP).
      11             :  *
      12             :  * The "TAP" handshake is the first one that was widely used in Tor: It
      13             :  * combines RSA1024-OAEP and AES128-CTR to perform a hybrid encryption over
      14             :  * the first message DH1024 key exchange.  (The RSA-encrypted part of the
      15             :  * encryption is authenticated; the AES-encrypted part isn't. This was
      16             :  * not a smart choice.)
      17             :  *
      18             :  * We didn't call it "TAP" ourselves -- Ian Goldberg named it in "On the
      19             :  * Security of the Tor Authentication Protocol".  (Spoiler: it's secure, but
      20             :  * its security is kind of fragile and implementation dependent.  Never modify
      21             :  * this implementation without reading and understanding that paper at least.)
      22             :  *
      23             :  * We have deprecated TAP since the ntor handshake came into general use.  It
      24             :  * is still used for hidden service IP and RP connections, however.
      25             :  *
      26             :  * This handshake, like the other circuit-extension handshakes, is
      27             :  * invoked from onion.c.
      28             :  **/
      29             : 
      30             : #include "core/or/or.h"
      31             : #include "app/config/config.h"
      32             : #include "lib/crypt_ops/crypto_dh.h"
      33             : #include "lib/crypt_ops/crypto_rand.h"
      34             : #include "lib/crypt_ops/crypto_util.h"
      35             : #include "core/crypto/onion_tap.h"
      36             : #include "feature/stats/rephist.h"
      37             : 
      38             : /*----------------------------------------------------------------------*/
      39             : 
      40             : /** Given a router's 128 byte public key,
      41             :  * stores the following in onion_skin_out:
      42             :  *   - [42 bytes] OAEP padding
      43             :  *   - [16 bytes] Symmetric key for encrypting blob past RSA
      44             :  *   - [70 bytes] g^x part 1 (inside the RSA)
      45             :  *   - [58 bytes] g^x part 2 (symmetrically encrypted)
      46             :  *
      47             :  * Stores the DH private key into handshake_state_out for later completion
      48             :  * of the handshake.
      49             :  *
      50             :  * The meeting point/cookies and auth are zeroed out for now.
      51             :  */
      52             : int
      53           2 : onion_skin_TAP_create(crypto_pk_t *dest_router_key,
      54             :                   crypto_dh_t **handshake_state_out,
      55             :                   char *onion_skin_out) /* TAP_ONIONSKIN_CHALLENGE_LEN bytes */
      56             : {
      57           2 :   char challenge[DH1024_KEY_LEN];
      58           2 :   crypto_dh_t *dh = NULL;
      59           2 :   int dhbytes, pkbytes;
      60             : 
      61           2 :   tor_assert(dest_router_key);
      62           2 :   tor_assert(handshake_state_out);
      63           2 :   tor_assert(onion_skin_out);
      64           2 :   *handshake_state_out = NULL;
      65           2 :   memset(onion_skin_out, 0, TAP_ONIONSKIN_CHALLENGE_LEN);
      66             : 
      67           2 :   if (!(dh = crypto_dh_new(DH_TYPE_CIRCUIT)))
      68           0 :     goto err;
      69             : 
      70           2 :   dhbytes = crypto_dh_get_bytes(dh);
      71           2 :   pkbytes = (int) crypto_pk_keysize(dest_router_key);
      72           2 :   tor_assert(dhbytes == 128);
      73           2 :   tor_assert(pkbytes == 128);
      74             : 
      75           2 :   if (crypto_dh_get_public(dh, challenge, dhbytes))
      76           0 :     goto err;
      77             : 
      78             :   /* set meeting point, meeting cookie, etc here. Leave zero for now. */
      79           2 :   if (crypto_pk_obsolete_public_hybrid_encrypt(dest_router_key, onion_skin_out,
      80             :                                       TAP_ONIONSKIN_CHALLENGE_LEN,
      81             :                                       challenge, DH1024_KEY_LEN,
      82             :                                       PK_PKCS1_OAEP_PADDING, 1)<0)
      83           0 :     goto err;
      84             : 
      85           2 :   memwipe(challenge, 0, sizeof(challenge));
      86           2 :   *handshake_state_out = dh;
      87             : 
      88           2 :   return 0;
      89           0 :  err:
      90             :   /* LCOV_EXCL_START
      91             :    * We only get here if RSA encryption fails or DH keygen fails. Those
      92             :    * shouldn't be possible. */
      93             :   memwipe(challenge, 0, sizeof(challenge));
      94             :   if (dh) crypto_dh_free(dh);
      95             :   return -1;
      96             :   /* LCOV_EXCL_STOP */
      97             : }
      98             : 
      99             : /** Given an encrypted DH public key as generated by onion_skin_create,
     100             :  * and the private key for this onion router, generate the reply (128-byte
     101             :  * DH plus the first 20 bytes of shared key material), and store the
     102             :  * next key_out_len bytes of key material in key_out.
     103             :  */
     104             : int
     105           8 : onion_skin_TAP_server_handshake(
     106             :                             /*TAP_ONIONSKIN_CHALLENGE_LEN*/
     107             :                             const char *onion_skin,
     108             :                             crypto_pk_t *private_key,
     109             :                             crypto_pk_t *prev_private_key,
     110             :                             /*TAP_ONIONSKIN_REPLY_LEN*/
     111             :                             char *handshake_reply_out,
     112             :                             char *key_out,
     113             :                             size_t key_out_len)
     114             : {
     115           8 :   char challenge[TAP_ONIONSKIN_CHALLENGE_LEN];
     116           8 :   crypto_dh_t *dh = NULL;
     117           8 :   ssize_t len;
     118           8 :   char *key_material=NULL;
     119           8 :   size_t key_material_len=0;
     120           8 :   int i;
     121           8 :   crypto_pk_t *k;
     122             : 
     123           8 :   len = -1;
     124          11 :   for (i=0;i<2;++i) {
     125          11 :     k = i==0?private_key:prev_private_key;
     126          11 :     if (!k)
     127             :       break;
     128           9 :     len = crypto_pk_obsolete_private_hybrid_decrypt(k, challenge,
     129             :                                            TAP_ONIONSKIN_CHALLENGE_LEN,
     130             :                                            onion_skin,
     131             :                                            TAP_ONIONSKIN_CHALLENGE_LEN,
     132             :                                            PK_PKCS1_OAEP_PADDING,0);
     133           9 :     if (len>0)
     134             :       break;
     135             :   }
     136           8 :   if (len<0) {
     137           2 :     log_info(LD_PROTOCOL,
     138             :              "Couldn't decrypt onionskin: client may be using old onion key");
     139           2 :     goto err;
     140           6 :   } else if (len != DH1024_KEY_LEN) {
     141           1 :     log_fn(LOG_PROTOCOL_WARN, LD_PROTOCOL,
     142             :            "Unexpected onionskin length after decryption: %ld",
     143             :            (long)len);
     144           1 :     goto err;
     145             :   }
     146             : 
     147           5 :   dh = crypto_dh_new(DH_TYPE_CIRCUIT);
     148           5 :   if (!dh) {
     149             :     /* LCOV_EXCL_START
     150             :      * Failure to allocate a DH key should be impossible.
     151             :      */
     152             :     log_warn(LD_BUG, "Couldn't allocate DH key");
     153             :     goto err;
     154             :     /* LCOV_EXCL_STOP */
     155             :   }
     156           5 :   if (crypto_dh_get_public(dh, handshake_reply_out, DH1024_KEY_LEN)) {
     157             :     /* LCOV_EXCL_START
     158             :      * This can only fail if the length of the key we just allocated is too
     159             :      * big. That should be impossible. */
     160             :     log_info(LD_GENERAL, "crypto_dh_get_public failed.");
     161             :     goto err;
     162             :     /* LCOV_EXCL_STOP */
     163             :   }
     164             : 
     165           5 :   key_material_len = DIGEST_LEN+key_out_len;
     166           5 :   key_material = tor_malloc(key_material_len);
     167           5 :   len = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, dh, challenge,
     168             :                                  DH1024_KEY_LEN, key_material,
     169             :                                  key_material_len);
     170           5 :   if (len < 0) {
     171           1 :     log_info(LD_GENERAL, "crypto_dh_compute_secret failed.");
     172           1 :     goto err;
     173             :   }
     174             : 
     175             :   /* send back H(K|0) as proof that we learned K. */
     176           4 :   memcpy(handshake_reply_out+DH1024_KEY_LEN, key_material, DIGEST_LEN);
     177             : 
     178             :   /* use the rest of the key material for our shared keys, digests, etc */
     179           4 :   memcpy(key_out, key_material+DIGEST_LEN, key_out_len);
     180             : 
     181           4 :   memwipe(challenge, 0, sizeof(challenge));
     182           4 :   memwipe(key_material, 0, key_material_len);
     183           4 :   tor_free(key_material);
     184           4 :   crypto_dh_free(dh);
     185           4 :   return 0;
     186           4 :  err:
     187           4 :   memwipe(challenge, 0, sizeof(challenge));
     188           4 :   if (key_material) {
     189           1 :     memwipe(key_material, 0, key_material_len);
     190           1 :     tor_free(key_material);
     191             :   }
     192           4 :   if (dh) crypto_dh_free(dh);
     193             : 
     194             :   return -1;
     195             : }
     196             : 
     197             : /** Finish the client side of the DH handshake.
     198             :  * Given the 128 byte DH reply + 20 byte hash as generated by
     199             :  * onion_skin_server_handshake and the handshake state generated by
     200             :  * onion_skin_create, verify H(K) with the first 20 bytes of shared
     201             :  * key material, then generate key_out_len more bytes of shared key
     202             :  * material and store them in key_out.
     203             :  *
     204             :  * After the invocation, call crypto_dh_free on handshake_state.
     205             :  */
     206             : int
     207           6 : onion_skin_TAP_client_handshake(crypto_dh_t *handshake_state,
     208             :             const char *handshake_reply, /* TAP_ONIONSKIN_REPLY_LEN bytes */
     209             :             char *key_out,
     210             :             size_t key_out_len,
     211             :             const char **msg_out)
     212             : {
     213           6 :   ssize_t len;
     214           6 :   char *key_material=NULL;
     215           6 :   size_t key_material_len;
     216           6 :   tor_assert(crypto_dh_get_bytes(handshake_state) == DH1024_KEY_LEN);
     217             : 
     218           6 :   key_material_len = DIGEST_LEN + key_out_len;
     219           6 :   key_material = tor_malloc(key_material_len);
     220           6 :   len = crypto_dh_compute_secret(LOG_PROTOCOL_WARN, handshake_state,
     221             :                                  handshake_reply, DH1024_KEY_LEN, key_material,
     222             :                                  key_material_len);
     223           6 :   if (len < 0) {
     224           1 :     if (msg_out)
     225           1 :       *msg_out = "DH computation failed.";
     226           1 :     goto err;
     227             :   }
     228             : 
     229           5 :   if (tor_memneq(key_material, handshake_reply+DH1024_KEY_LEN, DIGEST_LEN)) {
     230             :     /* H(K) does *not* match. Something fishy. */
     231           1 :     if (msg_out)
     232           1 :       *msg_out = "Digest DOES NOT MATCH on onion handshake. Bug or attack.";
     233           1 :     goto err;
     234             :   }
     235             : 
     236             :   /* use the rest of the key material for our shared keys, digests, etc */
     237           4 :   memcpy(key_out, key_material+DIGEST_LEN, key_out_len);
     238             : 
     239           4 :   memwipe(key_material, 0, key_material_len);
     240           4 :   tor_free(key_material);
     241           4 :   return 0;
     242           2 :  err:
     243           2 :   memwipe(key_material, 0, key_material_len);
     244           2 :   tor_free(key_material);
     245           2 :   return -1;
     246             : }

Generated by: LCOV version 1.14