tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

relay_crypto_tor1.c (9735B)


      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 relay_crypto_tor1.c
      9 * @brief Implementation for legacy (tor1) relay cell encryption.
     10 **/
     11 
     12 #include "core/or/or.h"
     13 #include "lib/crypt_ops/crypto_cipher.h"
     14 #include "lib/crypt_ops/crypto_util.h"
     15 #include "core/crypto/hs_ntor.h" // for HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN
     16 #include "core/crypto/relay_crypto_tor1.h"
     17 #include "lib/cc/ctassert.h"
     18 
     19 #include "core/or/cell_st.h"
     20 #include "core/crypto/relay_crypto_st.h"
     21 
     22 /* Offset of digest within relay cell body for v0 cells. */
     23 #define V0_DIGEST_OFFSET 5
     24 #define V0_DIGEST_LEN 4
     25 #define V0_RECOGNIZED_OFFSET 1
     26 
     27 /** Update digest from the payload of cell. Assign integrity part to
     28 * cell.  Record full 20-byte digest in `buf`.
     29 */
     30 static void
     31 tor1_set_digest_v0(crypto_digest_t *digest, cell_t *cell, uint8_t *buf)
     32 {
     33  crypto_digest_add_bytes(digest, (char*)cell->payload, CELL_PAYLOAD_SIZE);
     34  crypto_digest_get_digest(digest, (char*)buf, DIGEST_LEN);
     35 //  log_fn(LOG_DEBUG,"Putting digest of %u %u %u %u into relay cell.",
     36 //    integrity[0], integrity[1], integrity[2], integrity[3]);
     37  memcpy(cell->payload + V0_DIGEST_OFFSET, buf, V0_DIGEST_LEN);
     38 }
     39 
     40 /** Does the digest for this circuit indicate that this cell is for us?
     41 *
     42 * Update digest from the payload of cell (with the integrity part set
     43 * to 0). If the integrity part is valid,
     44 * return 1 and save the full digest in the 20-byte buffer `buf`,
     45 * else restore digest
     46 * and cell to their original state and return 0.
     47 */
     48 static int
     49 tor1_relay_digest_matches_v0(crypto_digest_t *digest, cell_t *cell,
     50                             uint8_t *buf)
     51 {
     52  uint32_t received_integrity, calculated_integrity;
     53  uint8_t calculated_digest[DIGEST_LEN];
     54  crypto_digest_checkpoint_t backup_digest;
     55 
     56  CTASSERT(sizeof(uint32_t) == V0_DIGEST_LEN);
     57 
     58  crypto_digest_checkpoint(&backup_digest, digest);
     59 
     60  memcpy(&received_integrity, cell->payload + V0_DIGEST_OFFSET, V0_DIGEST_LEN);
     61  memset(cell->payload + V0_DIGEST_OFFSET, 0, V0_DIGEST_LEN);
     62 
     63 //  log_fn(LOG_DEBUG,"Reading digest of %u %u %u %u from relay cell.",
     64 //    received_integrity[0], received_integrity[1],
     65 //    received_integrity[2], received_integrity[3]);
     66 
     67  crypto_digest_add_bytes(digest, (char*) cell->payload, CELL_PAYLOAD_SIZE);
     68  crypto_digest_get_digest(digest, (char*) calculated_digest, DIGEST_LEN);
     69  calculated_integrity = get_uint32(calculated_digest);
     70 
     71  int rv = 1;
     72 
     73  if (calculated_integrity != received_integrity) {
     74 //    log_fn(LOG_INFO,"Recognized=0 but bad digest. Not recognizing.");
     75 // (%d vs %d).", received_integrity, calculated_integrity);
     76    /* restore digest to its old form */
     77    crypto_digest_restore(digest, &backup_digest);
     78    /* restore the relay header */
     79    memcpy(cell->payload + V0_DIGEST_OFFSET, &received_integrity,
     80           V0_DIGEST_LEN);
     81    rv = 0;
     82  } else {
     83    memcpy(buf, calculated_digest, DIGEST_LEN);
     84  }
     85 
     86  memwipe(&backup_digest, 0, sizeof(backup_digest));
     87  return rv;
     88 }
     89 
     90 static inline bool
     91 relay_cell_is_recognized_v0(const cell_t *cell)
     92 {
     93  return get_uint16(cell->payload + V0_RECOGNIZED_OFFSET) == 0;
     94 }
     95 
     96 /** Apply <b>cipher</b> to CELL_PAYLOAD_SIZE bytes of <b>in</b>
     97 * (in place).
     98 *
     99 * Note that we use the same operation for encrypting and for decrypting.
    100 */
    101 static void
    102 tor1_crypt_one_payload(crypto_cipher_t *cipher, uint8_t *in)
    103 {
    104  crypto_cipher_crypt_inplace(cipher, (char*) in, CELL_PAYLOAD_SIZE);
    105 }
    106 
    107 /** Encrypt and authenticate `cell` using the cryptographic
    108 * material in `tor1`.
    109 *
    110 * This method should be used for the first encryption performed
    111 * by the client - that is, the one corresponding to the exit node.
    112 */
    113 void
    114 tor1_crypt_client_originate(tor1_crypt_t *tor1,
    115                            cell_t *cell)
    116 {
    117  tor1_set_digest_v0(tor1->f_digest, cell, tor1->sendme_digest);
    118  tor1_crypt_one_payload(tor1->f_crypto, cell->payload);
    119 }
    120 
    121 /** Encrypt and authenticate `cell`, using the cryptographic
    122 * material in `tor1`.
    123 *
    124 * This method should be used by relays when originating cells toward the
    125 * client.
    126 */
    127 void
    128 tor1_crypt_relay_originate(tor1_crypt_t *tor1,
    129                           cell_t *cell)
    130 {
    131  tor1_set_digest_v0(tor1->b_digest, cell, tor1->sendme_digest);
    132  tor1_crypt_one_payload(tor1->b_crypto, cell->payload);
    133 }
    134 
    135 /** Encrypt `cell` using the cryptographic material in `tor1`.
    136 *
    137 * This method should be used by clients for cryptographic layers
    138 * that are _not_ the final recipient of the cell. */
    139 void
    140 tor1_crypt_client_forward(tor1_crypt_t *tor1, cell_t *cell)
    141 {
    142  tor1_crypt_one_payload(tor1->f_crypto, cell->payload);
    143 }
    144 
    145 /** Encrypt `cell` using the cryptographic material in `tor1`.
    146 *
    147 * This method should be used by relays on cells that are moving
    148 * toward the client. */
    149 void
    150 tor1_crypt_relay_backward(tor1_crypt_t *tor1, cell_t *cell)
    151 {
    152  tor1_crypt_one_payload(tor1->b_crypto, cell->payload);
    153 }
    154 
    155 /** Decrypt `cell` using the cryptographic material in `tor1`.
    156 *
    157 * Return `true` when we are the destination for this cell.
    158 *
    159 * This method should be used by relays on cells
    160 * that are moving away from the client. */
    161 bool
    162 tor1_crypt_relay_forward(tor1_crypt_t *tor1, cell_t *cell)
    163 {
    164  tor1_crypt_one_payload(tor1->f_crypto, cell->payload);
    165  if (relay_cell_is_recognized_v0(cell)) {
    166    if (tor1_relay_digest_matches_v0(tor1->f_digest, cell,
    167                                     tor1->sendme_digest)) {
    168      return true;
    169    }
    170  }
    171  return false;
    172 }
    173 
    174 /** Decrypt `cell` using  the cryptographic material in `tor1`.
    175 *
    176 * Return `true` when this cell is recognized and authenticated
    177 * as coming from the relay that also holds this cryptographic material.
    178 *
    179 * This method should be used by clients on incoming cells. */
    180 bool
    181 tor1_crypt_client_backward(tor1_crypt_t *tor1, cell_t *cell)
    182 {
    183  tor1_crypt_one_payload(tor1->b_crypto, cell->payload);
    184 
    185  if (relay_cell_is_recognized_v0(cell)) {
    186    if (tor1_relay_digest_matches_v0(tor1->b_digest, cell,
    187                                     tor1->sendme_digest)) {
    188      return true;
    189    }
    190  }
    191  return false;
    192 }
    193 
    194 /** Return the number of bytes that tor1_crypt_init expects. */
    195 size_t
    196 tor1_key_material_len(bool is_hs)
    197 {
    198  if (is_hs)
    199    return HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN;
    200  else
    201    return CPATH_KEY_MATERIAL_LEN;
    202 }
    203 
    204 /** Initialize <b>crypto</b> from the key material in key_data.
    205 *
    206 * If <b>is_hs_v3</b> is set, this cpath will be used for next gen hidden
    207 * service circuits and <b>key_data</b> must be
    208 * HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN bytes in length.
    209 *
    210 * If <b>is_hs_v3</b> is not set, key_data must contain CPATH_KEY_MATERIAL_LEN
    211 * bytes, which are used as follows:
    212 *   - 20 to initialize f_digest
    213 *   - 20 to initialize b_digest
    214 *   - 16 to key f_crypto
    215 *   - 16 to key b_crypto
    216 *
    217 * (If 'reverse' is true, then f_XX and b_XX are swapped.)
    218 *
    219 * Return 0 if init was successful, else -1 if it failed.
    220 */
    221 int
    222 tor1_crypt_init(tor1_crypt_t *crypto,
    223                 const char *key_data, size_t key_data_len,
    224                 int reverse, int is_hs_v3)
    225 {
    226  crypto_digest_t *tmp_digest;
    227  crypto_cipher_t *tmp_crypto;
    228  size_t digest_len = 0;
    229  size_t cipher_key_len = 0;
    230 
    231  tor_assert(crypto);
    232  tor_assert(key_data);
    233  tor_assert(!(crypto->f_crypto || crypto->b_crypto ||
    234             crypto->f_digest || crypto->b_digest));
    235 
    236  /* Basic key size validation */
    237  if (is_hs_v3 && BUG(key_data_len != HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN)) {
    238    goto err;
    239  } else if (!is_hs_v3 && BUG(key_data_len != CPATH_KEY_MATERIAL_LEN)) {
    240    goto err;
    241  }
    242 
    243  /* If we are using this crypto for next gen onion services use SHA3-256,
    244     otherwise use good ol' SHA1 */
    245  if (is_hs_v3) {
    246    digest_len = DIGEST256_LEN;
    247    cipher_key_len = CIPHER256_KEY_LEN;
    248    crypto->f_digest = crypto_digest256_new(DIGEST_SHA3_256);
    249    crypto->b_digest = crypto_digest256_new(DIGEST_SHA3_256);
    250  } else {
    251    digest_len = DIGEST_LEN;
    252    cipher_key_len = CIPHER_KEY_LEN;
    253    crypto->f_digest = crypto_digest_new();
    254    crypto->b_digest = crypto_digest_new();
    255  }
    256 
    257  tor_assert(digest_len != 0);
    258  tor_assert(cipher_key_len != 0);
    259  const int cipher_key_bits = (int) cipher_key_len * 8;
    260 
    261  crypto_digest_add_bytes(crypto->f_digest, key_data, digest_len);
    262  crypto_digest_add_bytes(crypto->b_digest, key_data+digest_len, digest_len);
    263 
    264  crypto->f_crypto = crypto_cipher_new_with_bits(key_data+(2*digest_len),
    265                                                cipher_key_bits);
    266  if (!crypto->f_crypto) {
    267    log_warn(LD_BUG,"Forward cipher initialization failed.");
    268    goto err;
    269  }
    270 
    271  crypto->b_crypto = crypto_cipher_new_with_bits(
    272                                        key_data+(2*digest_len)+cipher_key_len,
    273                                        cipher_key_bits);
    274  if (!crypto->b_crypto) {
    275    log_warn(LD_BUG,"Backward cipher initialization failed.");
    276    goto err;
    277  }
    278 
    279  if (reverse) {
    280    tmp_digest = crypto->f_digest;
    281    crypto->f_digest = crypto->b_digest;
    282    crypto->b_digest = tmp_digest;
    283    tmp_crypto = crypto->f_crypto;
    284    crypto->f_crypto = crypto->b_crypto;
    285    crypto->b_crypto = tmp_crypto;
    286  }
    287 
    288  return 0;
    289 err:
    290  tor1_crypt_clear(crypto);
    291  return -1;
    292 }
    293 
    294 /** Assert that <b>crypto</b> is valid and set. */
    295 void
    296 tor1_crypt_assert_ok(const tor1_crypt_t *crypto)
    297 {
    298  tor_assert(crypto->f_crypto);
    299  tor_assert(crypto->b_crypto);
    300  tor_assert(crypto->f_digest);
    301  tor_assert(crypto->b_digest);
    302 }
    303 
    304 void
    305 tor1_crypt_clear(tor1_crypt_t *crypto)
    306 {
    307  if (BUG(!crypto))
    308    return;
    309  crypto_cipher_free(crypto->f_crypto);
    310  crypto_cipher_free(crypto->b_crypto);
    311  crypto_digest_free(crypto->f_digest);
    312  crypto_digest_free(crypto->b_digest);
    313 }