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

          Line data    Source code
       1             : /* Copyright (c) 2017-2021, The Tor Project, Inc. */
       2             : /* See LICENSE for licensing information */
       3             : 
       4             : /** \file hs_ntor.c
       5             :  *  \brief Implements the ntor variant used in Tor hidden services.
       6             :  *
       7             :  *  \details
       8             :  *  This module handles the variant of the ntor handshake that is documented in
       9             :  *  section [NTOR-WITH-EXTRA-DATA] of rend-spec-ng.txt .
      10             :  *
      11             :  *  The functions in this file provide an API that should be used when sending
      12             :  *  or receiving INTRODUCE1/RENDEZVOUS1 cells to generate the various key
      13             :  *  material required to create and handle those cells.
      14             :  *
      15             :  *  In the case of INTRODUCE1 it provides encryption and MAC keys to
      16             :  *  encode/decode the encrypted blob (see hs_ntor_intro_cell_keys_t). The
      17             :  *  relevant pub functions are hs_ntor_{client,service}_get_introduce1_keys().
      18             :  *
      19             :  *  In the case of RENDEZVOUS1 it calculates the MAC required to authenticate
      20             :  *  the cell, and also provides the key seed that is used to derive the crypto
      21             :  *  material for rendezvous encryption (see hs_ntor_rend_cell_keys_t). The
      22             :  *  relevant pub functions are hs_ntor_{client,service}_get_rendezvous1_keys().
      23             :  *  It also provides a function (hs_ntor_circuit_key_expansion()) that does the
      24             :  *  rendezvous key expansion to setup end-to-end rend circuit keys.
      25             :  */
      26             : 
      27             : #include "core/or/or.h"
      28             : #include "lib/crypt_ops/crypto_util.h"
      29             : #include "lib/crypt_ops/crypto_curve25519.h"
      30             : #include "lib/crypt_ops/crypto_ed25519.h"
      31             : #include "core/crypto/hs_ntor.h"
      32             : 
      33             : /* String constants used by the ntor HS protocol */
      34             : #define PROTOID "tor-hs-ntor-curve25519-sha3-256-1"
      35             : #define PROTOID_LEN (sizeof(PROTOID) - 1)
      36             : #define SERVER_STR "Server"
      37             : #define SERVER_STR_LEN (sizeof(SERVER_STR) - 1)
      38             : 
      39             : /* Protocol-specific tweaks to our crypto inputs */
      40             : #define T_HSENC PROTOID ":hs_key_extract"
      41             : #define T_HSENC_LEN (sizeof(T_HSENC) - 1)
      42             : #define T_HSVERIFY PROTOID ":hs_verify"
      43             : #define T_HSMAC PROTOID ":hs_mac"
      44             : #define M_HSEXPAND PROTOID ":hs_key_expand"
      45             : #define M_HSEXPAND_LEN (sizeof(M_HSEXPAND) - 1)
      46             : 
      47             : /************************* Helper functions: *******************************/
      48             : 
      49             : /** Helper macro: copy <b>len</b> bytes from <b>inp</b> to <b>ptr</b> and
      50             :  *advance <b>ptr</b> by the number of bytes copied. Stolen from onion_ntor.c */
      51             : #define APPEND(ptr, inp, len)                   \
      52             :   STMT_BEGIN {                                  \
      53             :     memcpy(ptr, (inp), (len));                  \
      54             :     ptr += len;                                 \
      55             :   } STMT_END
      56             : 
      57             : /* Length of EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID */
      58             : #define REND_SECRET_HS_INPUT_LEN (CURVE25519_OUTPUT_LEN * 2 + \
      59             :   ED25519_PUBKEY_LEN + CURVE25519_PUBKEY_LEN * 3 + PROTOID_LEN)
      60             : /* Length of auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server" */
      61             : #define REND_AUTH_INPUT_LEN (DIGEST256_LEN + ED25519_PUBKEY_LEN + \
      62             :   CURVE25519_PUBKEY_LEN * 3 + PROTOID_LEN + SERVER_STR_LEN)
      63             : 
      64             : /** Helper function: Compute the last part of the HS ntor handshake which
      65             :  *  derives key material necessary to create and handle RENDEZVOUS1
      66             :  *  cells. Function used by both client and service. The actual calculations is
      67             :  *  as follows:
      68             :  *
      69             :  *    NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc)
      70             :  *    verify = MAC(rend_secret_hs_input, t_hsverify)
      71             :  *    auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server"
      72             :  *    auth_input_mac = MAC(auth_input, t_hsmac)
      73             :  *
      74             :  *  where in the above, AUTH_KEY is <b>intro_auth_pubkey</b>, B is
      75             :  *  <b>intro_enc_pubkey</b>, Y is <b>service_ephemeral_rend_pubkey</b>, and X
      76             :  *  is <b>client_ephemeral_enc_pubkey</b>. The provided
      77             :  *  <b>rend_secret_hs_input</b> is of size REND_SECRET_HS_INPUT_LEN.
      78             :  *
      79             :  *  The final results of NTOR_KEY_SEED and auth_input_mac are placed in
      80             :  *  <b>hs_ntor_rend_cell_keys_out</b>. Return 0 if everything went fine. */
      81             : static int
      82           4 : get_rendezvous1_key_material(const uint8_t *rend_secret_hs_input,
      83             :                   const ed25519_public_key_t *intro_auth_pubkey,
      84             :                   const curve25519_public_key_t *intro_enc_pubkey,
      85             :                   const curve25519_public_key_t *service_ephemeral_rend_pubkey,
      86             :                   const curve25519_public_key_t *client_ephemeral_enc_pubkey,
      87             :                   hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out)
      88             : {
      89           4 :   int bad = 0;
      90           4 :   uint8_t ntor_key_seed[DIGEST256_LEN];
      91           4 :   uint8_t ntor_verify[DIGEST256_LEN];
      92           4 :   uint8_t rend_auth_input[REND_AUTH_INPUT_LEN];
      93           4 :   uint8_t rend_cell_auth[DIGEST256_LEN];
      94           4 :   uint8_t *ptr;
      95             : 
      96             :   /* Let's build NTOR_KEY_SEED */
      97           4 :   crypto_mac_sha3_256(ntor_key_seed, sizeof(ntor_key_seed),
      98             :                       rend_secret_hs_input, REND_SECRET_HS_INPUT_LEN,
      99             :                       (const uint8_t *)T_HSENC, strlen(T_HSENC));
     100           4 :   bad |= safe_mem_is_zero(ntor_key_seed, DIGEST256_LEN);
     101             : 
     102             :   /* Let's build ntor_verify */
     103           4 :   crypto_mac_sha3_256(ntor_verify, sizeof(ntor_verify),
     104             :                       rend_secret_hs_input, REND_SECRET_HS_INPUT_LEN,
     105             :                       (const uint8_t *)T_HSVERIFY, strlen(T_HSVERIFY));
     106           4 :   bad |= safe_mem_is_zero(ntor_verify, DIGEST256_LEN);
     107             : 
     108             :   /* Let's build auth_input: */
     109           4 :   ptr = rend_auth_input;
     110             :   /* Append ntor_verify */
     111           4 :   APPEND(ptr, ntor_verify, sizeof(ntor_verify));
     112             :   /* Append AUTH_KEY */
     113           4 :   APPEND(ptr, intro_auth_pubkey->pubkey, ED25519_PUBKEY_LEN);
     114             :   /* Append B */
     115           4 :   APPEND(ptr, intro_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
     116             :   /* Append Y */
     117           4 :   APPEND(ptr,
     118             :          service_ephemeral_rend_pubkey->public_key, CURVE25519_PUBKEY_LEN);
     119             :   /* Append X */
     120           4 :   APPEND(ptr,
     121             :          client_ephemeral_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
     122             :   /* Append PROTOID */
     123           4 :   APPEND(ptr, PROTOID, strlen(PROTOID));
     124             :   /* Append "Server" */
     125           4 :   APPEND(ptr, SERVER_STR, strlen(SERVER_STR));
     126           4 :   tor_assert(ptr == rend_auth_input + sizeof(rend_auth_input));
     127             : 
     128             :   /* Let's build auth_input_mac that goes in RENDEZVOUS1 cell */
     129           4 :   crypto_mac_sha3_256(rend_cell_auth, sizeof(rend_cell_auth),
     130             :                       rend_auth_input, sizeof(rend_auth_input),
     131             :                       (const uint8_t *)T_HSMAC, strlen(T_HSMAC));
     132           4 :   bad |= safe_mem_is_zero(ntor_verify, DIGEST256_LEN);
     133             : 
     134             :   { /* Get the computed RENDEZVOUS1 material! */
     135           4 :     memcpy(&hs_ntor_rend_cell_keys_out->rend_cell_auth_mac,
     136             :            rend_cell_auth, DIGEST256_LEN);
     137           4 :     memcpy(&hs_ntor_rend_cell_keys_out->ntor_key_seed,
     138             :            ntor_key_seed, DIGEST256_LEN);
     139             :   }
     140             : 
     141           4 :   memwipe(rend_cell_auth, 0, sizeof(rend_cell_auth));
     142           4 :   memwipe(rend_auth_input, 0, sizeof(rend_auth_input));
     143           4 :   memwipe(ntor_key_seed, 0, sizeof(ntor_key_seed));
     144             : 
     145           4 :   return bad;
     146             : }
     147             : 
     148             : /** Length of secret_input = EXP(B,x) | AUTH_KEY | X | B | PROTOID */
     149             : #define INTRO_SECRET_HS_INPUT_LEN (CURVE25519_OUTPUT_LEN +ED25519_PUBKEY_LEN +\
     150             :   CURVE25519_PUBKEY_LEN + CURVE25519_PUBKEY_LEN + PROTOID_LEN)
     151             : /* Length of info = m_hsexpand | subcredential */
     152             : #define INFO_BLOB_LEN (M_HSEXPAND_LEN + DIGEST256_LEN)
     153             : /* Length of KDF input = intro_secret_hs_input | t_hsenc | info */
     154             : #define KDF_INPUT_LEN (INTRO_SECRET_HS_INPUT_LEN + T_HSENC_LEN + INFO_BLOB_LEN)
     155             : 
     156             : /** Helper function: Compute the part of the HS ntor handshake that generates
     157             :  *  key material for creating and handling INTRODUCE1 cells. Function used
     158             :  *  by both client and service. Specifically, calculate the following:
     159             :  *
     160             :  *     info = m_hsexpand | subcredential
     161             :  *     hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN)
     162             :  *     ENC_KEY = hs_keys[0:S_KEY_LEN]
     163             :  *     MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN]
     164             :  *
     165             :  *  where intro_secret_hs_input is <b>secret_input</b> (of size
     166             :  *  INTRO_SECRET_HS_INPUT_LEN), and <b>subcredential</b> is of size
     167             :  *  DIGEST256_LEN.
     168             :  *
     169             :  * If everything went well, fill <b>hs_ntor_intro_cell_keys_out</b> with the
     170             :  * necessary key material, and return 0. */
     171             : static void
     172          33 : get_introduce1_key_material(const uint8_t *secret_input,
     173             :                         const hs_subcredential_t *subcredential,
     174             :                         hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
     175             : {
     176          33 :   uint8_t keystream[CIPHER256_KEY_LEN + DIGEST256_LEN];
     177          33 :   uint8_t info_blob[INFO_BLOB_LEN];
     178          33 :   uint8_t kdf_input[KDF_INPUT_LEN];
     179          33 :   uint8_t *ptr;
     180             : 
     181             :   /* Let's build info */
     182          33 :   ptr = info_blob;
     183          33 :   APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND));
     184          33 :   APPEND(ptr, subcredential->subcred, SUBCRED_LEN);
     185          33 :   tor_assert(ptr == info_blob + sizeof(info_blob));
     186             : 
     187             :   /* Let's build the input to the KDF */
     188          33 :   ptr = kdf_input;
     189          33 :   APPEND(ptr, secret_input, INTRO_SECRET_HS_INPUT_LEN);
     190          33 :   APPEND(ptr, T_HSENC, strlen(T_HSENC));
     191          33 :   APPEND(ptr, info_blob, sizeof(info_blob));
     192          33 :   tor_assert(ptr == kdf_input + sizeof(kdf_input));
     193             : 
     194             :   /* Now we need to run kdf_input over SHAKE-256 */
     195          33 :   crypto_xof(keystream, sizeof(keystream),
     196             :              kdf_input, sizeof(kdf_input));
     197             : 
     198             :   { /* Get the keys */
     199          33 :     memcpy(&hs_ntor_intro_cell_keys_out->enc_key, keystream,CIPHER256_KEY_LEN);
     200          33 :     memcpy(&hs_ntor_intro_cell_keys_out->mac_key,
     201             :            keystream+CIPHER256_KEY_LEN, DIGEST256_LEN);
     202             :   }
     203             : 
     204          33 :   memwipe(keystream,  0, sizeof(keystream));
     205          33 :   memwipe(kdf_input,  0, sizeof(kdf_input));
     206          33 : }
     207             : 
     208             : /** Helper function: Calculate the 'intro_secret_hs_input' element used by the
     209             :  * HS ntor handshake and place it in <b>secret_input_out</b>. This function is
     210             :  * used by both client and service code.
     211             :  *
     212             :  * For the client-side it looks like this:
     213             :  *
     214             :  *         intro_secret_hs_input = EXP(B,x) | AUTH_KEY | X | B | PROTOID
     215             :  *
     216             :  * whereas for the service-side it looks like this:
     217             :  *
     218             :  *         intro_secret_hs_input = EXP(X,b) | AUTH_KEY | X | B | PROTOID
     219             :  *
     220             :  * In this function, <b>dh_result</b> carries the EXP() result (and has size
     221             :  * CURVE25519_OUTPUT_LEN) <b>intro_auth_pubkey</b> is AUTH_KEY,
     222             :  * <b>client_ephemeral_enc_pubkey</b> is X, and <b>intro_enc_pubkey</b> is B.
     223             :  */
     224             : static void
     225          13 : get_intro_secret_hs_input(const uint8_t *dh_result,
     226             :                     const ed25519_public_key_t *intro_auth_pubkey,
     227             :                     const curve25519_public_key_t *client_ephemeral_enc_pubkey,
     228             :                     const curve25519_public_key_t *intro_enc_pubkey,
     229             :                     uint8_t *secret_input_out)
     230             : {
     231          13 :   uint8_t *ptr;
     232             : 
     233             :   /* Append EXP() */
     234          13 :   ptr = secret_input_out;
     235          13 :   APPEND(ptr, dh_result, CURVE25519_OUTPUT_LEN);
     236             :   /* Append AUTH_KEY */
     237          13 :   APPEND(ptr, intro_auth_pubkey->pubkey, ED25519_PUBKEY_LEN);
     238             :   /* Append X */
     239          13 :   APPEND(ptr, client_ephemeral_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
     240             :   /* Append B */
     241          13 :   APPEND(ptr, intro_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
     242             :   /* Append PROTOID */
     243          13 :   APPEND(ptr, PROTOID, strlen(PROTOID));
     244          13 :   tor_assert(ptr == secret_input_out + INTRO_SECRET_HS_INPUT_LEN);
     245          13 : }
     246             : 
     247             : /** Calculate the 'rend_secret_hs_input' element used by the HS ntor handshake
     248             :  *  and place it in <b>rend_secret_hs_input_out</b>. This function is used by
     249             :  *  both client and service code.
     250             :  *
     251             :  * The computation on the client side is:
     252             :  *  rend_secret_hs_input = EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID
     253             :  * whereas on the service side it is:
     254             :  *  rend_secret_hs_input = EXP(Y,x) | EXP(B,x) | AUTH_KEY | B | X | Y | PROTOID
     255             :  *
     256             :  * where:
     257             :  * <b>dh_result1</b> and <b>dh_result2</b> carry the two EXP() results (of size
     258             :  * CURVE25519_OUTPUT_LEN)
     259             :  * <b>intro_auth_pubkey</b> is AUTH_KEY,
     260             :  * <b>intro_enc_pubkey</b> is B,
     261             :  * <b>client_ephemeral_enc_pubkey</b> is X, and
     262             :  * <b>service_ephemeral_rend_pubkey</b> is Y.
     263             :  */
     264             : static void
     265           4 : get_rend_secret_hs_input(const uint8_t *dh_result1, const uint8_t *dh_result2,
     266             :                   const ed25519_public_key_t *intro_auth_pubkey,
     267             :                   const curve25519_public_key_t *intro_enc_pubkey,
     268             :                   const curve25519_public_key_t *client_ephemeral_enc_pubkey,
     269             :                   const curve25519_public_key_t *service_ephemeral_rend_pubkey,
     270             :                   uint8_t *rend_secret_hs_input_out)
     271             : {
     272           4 :   uint8_t *ptr;
     273             : 
     274           4 :   ptr = rend_secret_hs_input_out;
     275             :   /* Append the first EXP() */
     276           4 :   APPEND(ptr, dh_result1, CURVE25519_OUTPUT_LEN);
     277             :   /* Append the other EXP() */
     278           4 :   APPEND(ptr, dh_result2, CURVE25519_OUTPUT_LEN);
     279             :   /* Append AUTH_KEY */
     280           4 :   APPEND(ptr, intro_auth_pubkey->pubkey, ED25519_PUBKEY_LEN);
     281             :   /* Append B */
     282           4 :   APPEND(ptr, intro_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
     283             :   /* Append X */
     284           4 :   APPEND(ptr,
     285             :          client_ephemeral_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN);
     286             :   /* Append Y */
     287           4 :   APPEND(ptr,
     288             :          service_ephemeral_rend_pubkey->public_key, CURVE25519_PUBKEY_LEN);
     289             :   /* Append PROTOID */
     290           4 :   APPEND(ptr, PROTOID, strlen(PROTOID));
     291           4 :   tor_assert(ptr == rend_secret_hs_input_out + REND_SECRET_HS_INPUT_LEN);
     292           4 : }
     293             : 
     294             : /************************* Public functions: *******************************/
     295             : 
     296             : /* Public function: Do the appropriate ntor calculations and derive the keys
     297             :  * needed to encrypt and authenticate INTRODUCE1 cells. Return 0 and place the
     298             :  * final key material in <b>hs_ntor_intro_cell_keys_out</b> if everything went
     299             :  * well, otherwise return -1;
     300             :  *
     301             :  * The relevant calculations are as follows:
     302             :  *
     303             :  *     intro_secret_hs_input = EXP(B,x) | AUTH_KEY | X | B | PROTOID
     304             :  *     info = m_hsexpand | subcredential
     305             :  *     hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN)
     306             :  *     ENC_KEY = hs_keys[0:S_KEY_LEN]
     307             :  *     MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN]
     308             :  *
     309             :  * where:
     310             :  * <b>intro_auth_pubkey</b> is AUTH_KEY (found in HS descriptor),
     311             :  * <b>intro_enc_pubkey</b> is B (also found in HS descriptor),
     312             :  * <b>client_ephemeral_enc_keypair</b> is freshly generated keypair (x,X)
     313             :  * <b>subcredential</b> is the hidden service subcredential (of size
     314             :  * DIGEST256_LEN). */
     315             : int
     316           5 : hs_ntor_client_get_introduce1_keys(
     317             :                       const ed25519_public_key_t *intro_auth_pubkey,
     318             :                       const curve25519_public_key_t *intro_enc_pubkey,
     319             :                       const curve25519_keypair_t *client_ephemeral_enc_keypair,
     320             :                       const hs_subcredential_t *subcredential,
     321             :                       hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
     322             : {
     323           5 :   int bad = 0;
     324           5 :   uint8_t secret_input[INTRO_SECRET_HS_INPUT_LEN];
     325           5 :   uint8_t dh_result[CURVE25519_OUTPUT_LEN];
     326             : 
     327           5 :   tor_assert(intro_auth_pubkey);
     328           5 :   tor_assert(intro_enc_pubkey);
     329           5 :   tor_assert(client_ephemeral_enc_keypair);
     330           5 :   tor_assert(subcredential);
     331           5 :   tor_assert(hs_ntor_intro_cell_keys_out);
     332             : 
     333             :   /* Calculate EXP(B,x) */
     334           5 :   curve25519_handshake(dh_result,
     335             :                        &client_ephemeral_enc_keypair->seckey,
     336             :                        intro_enc_pubkey);
     337           5 :   bad |= safe_mem_is_zero(dh_result, CURVE25519_OUTPUT_LEN);
     338             : 
     339             :   /* Get intro_secret_hs_input */
     340           5 :   get_intro_secret_hs_input(dh_result, intro_auth_pubkey,
     341             :                             &client_ephemeral_enc_keypair->pubkey,
     342             :                             intro_enc_pubkey, secret_input);
     343           5 :   bad |= safe_mem_is_zero(secret_input, CURVE25519_OUTPUT_LEN);
     344             : 
     345             :   /* Get ENC_KEY and MAC_KEY! */
     346           5 :   get_introduce1_key_material(secret_input, subcredential,
     347             :                               hs_ntor_intro_cell_keys_out);
     348             : 
     349             :   /* Cleanup */
     350           5 :   memwipe(secret_input,  0, sizeof(secret_input));
     351           5 :   if (bad) {
     352           0 :     memwipe(hs_ntor_intro_cell_keys_out, 0, sizeof(hs_ntor_intro_cell_keys_t));
     353             :   }
     354             : 
     355           5 :   return bad ? -1 : 0;
     356             : }
     357             : 
     358             : /* Public function: Do the appropriate ntor calculations and derive the keys
     359             :  * needed to verify RENDEZVOUS1 cells and encrypt further rendezvous
     360             :  * traffic. Return 0 and place the final key material in
     361             :  * <b>hs_ntor_rend_cell_keys_out</b> if everything went well, else return -1.
     362             :  *
     363             :  * The relevant calculations are as follows:
     364             :  *
     365             :  *  rend_secret_hs_input = EXP(Y,x) | EXP(B,x) | AUTH_KEY | B | X | Y | PROTOID
     366             :  *  NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc)
     367             :  *  verify = MAC(rend_secret_hs_input, t_hsverify)
     368             :  *  auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server"
     369             :  *  auth_input_mac = MAC(auth_input, t_hsmac)
     370             :  *
     371             :  * where:
     372             :  * <b>intro_auth_pubkey</b> is AUTH_KEY (found in HS descriptor),
     373             :  * <b>client_ephemeral_enc_keypair</b> is freshly generated keypair (x,X)
     374             :  * <b>intro_enc_pubkey</b> is B (also found in HS descriptor),
     375             :  * <b>service_ephemeral_rend_pubkey</b> is Y (SERVER_PK in RENDEZVOUS1 cell) */
     376             : int
     377           2 : hs_ntor_client_get_rendezvous1_keys(
     378             :                   const ed25519_public_key_t *intro_auth_pubkey,
     379             :                   const curve25519_keypair_t *client_ephemeral_enc_keypair,
     380             :                   const curve25519_public_key_t *intro_enc_pubkey,
     381             :                   const curve25519_public_key_t *service_ephemeral_rend_pubkey,
     382             :                   hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out)
     383             : {
     384           2 :   int bad = 0;
     385           2 :   uint8_t rend_secret_hs_input[REND_SECRET_HS_INPUT_LEN];
     386           2 :   uint8_t dh_result1[CURVE25519_OUTPUT_LEN];
     387           2 :   uint8_t dh_result2[CURVE25519_OUTPUT_LEN];
     388             : 
     389           2 :   tor_assert(intro_auth_pubkey);
     390           2 :   tor_assert(client_ephemeral_enc_keypair);
     391           2 :   tor_assert(intro_enc_pubkey);
     392           2 :   tor_assert(service_ephemeral_rend_pubkey);
     393           2 :   tor_assert(hs_ntor_rend_cell_keys_out);
     394             : 
     395             :   /* Compute EXP(Y, x) */
     396           2 :   curve25519_handshake(dh_result1,
     397             :                        &client_ephemeral_enc_keypair->seckey,
     398             :                        service_ephemeral_rend_pubkey);
     399           2 :   bad |= safe_mem_is_zero(dh_result1, CURVE25519_OUTPUT_LEN);
     400             : 
     401             :   /* Compute EXP(B, x) */
     402           2 :   curve25519_handshake(dh_result2,
     403             :                        &client_ephemeral_enc_keypair->seckey,
     404             :                        intro_enc_pubkey);
     405           2 :   bad |= safe_mem_is_zero(dh_result2, CURVE25519_OUTPUT_LEN);
     406             : 
     407             :   /* Get rend_secret_hs_input */
     408           2 :   get_rend_secret_hs_input(dh_result1, dh_result2,
     409             :                            intro_auth_pubkey, intro_enc_pubkey,
     410             :                            &client_ephemeral_enc_keypair->pubkey,
     411             :                            service_ephemeral_rend_pubkey,
     412             :                            rend_secret_hs_input);
     413             : 
     414             :   /* Get NTOR_KEY_SEED and the auth_input MAC */
     415           2 :   bad |= get_rendezvous1_key_material(rend_secret_hs_input,
     416             :                                       intro_auth_pubkey,
     417             :                                       intro_enc_pubkey,
     418             :                                       service_ephemeral_rend_pubkey,
     419             :                                       &client_ephemeral_enc_keypair->pubkey,
     420             :                                       hs_ntor_rend_cell_keys_out);
     421             : 
     422           2 :   memwipe(rend_secret_hs_input, 0, sizeof(rend_secret_hs_input));
     423           2 :   if (bad) {
     424           0 :     memwipe(hs_ntor_rend_cell_keys_out, 0, sizeof(hs_ntor_rend_cell_keys_t));
     425             :   }
     426             : 
     427           2 :   return bad ? -1 : 0;
     428             : }
     429             : 
     430             : /* Public function: Do the appropriate ntor calculations and derive the keys
     431             :  * needed to decrypt and verify INTRODUCE1 cells. Return 0 and place the final
     432             :  * key material in <b>hs_ntor_intro_cell_keys_out</b> if everything went well,
     433             :  * otherwise return -1;
     434             :  *
     435             :  * The relevant calculations are as follows:
     436             :  *
     437             :  *    intro_secret_hs_input = EXP(X,b) | AUTH_KEY | X | B | PROTOID
     438             :  *    info = m_hsexpand | subcredential
     439             :  *    hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN)
     440             :  *    HS_DEC_KEY = hs_keys[0:S_KEY_LEN]
     441             :  *    HS_MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN]
     442             :  *
     443             :  * where:
     444             :  * <b>intro_auth_pubkey</b> is AUTH_KEY (introduction point auth key),
     445             :  * <b>intro_enc_keypair</b> is (b,B) (introduction point encryption keypair),
     446             :  * <b>client_ephemeral_enc_pubkey</b> is X (CLIENT_PK in INTRODUCE2 cell),
     447             :  * <b>subcredential</b> is the HS subcredential (of size DIGEST256_LEN) */
     448             : int
     449           1 : hs_ntor_service_get_introduce1_keys(
     450             :                     const ed25519_public_key_t *intro_auth_pubkey,
     451             :                     const curve25519_keypair_t *intro_enc_keypair,
     452             :                     const curve25519_public_key_t *client_ephemeral_enc_pubkey,
     453             :                     const hs_subcredential_t *subcredential,
     454             :                     hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
     455             : {
     456           1 :   return hs_ntor_service_get_introduce1_keys_multi(
     457             :                              intro_auth_pubkey,
     458             :                              intro_enc_keypair,
     459             :                              client_ephemeral_enc_pubkey,
     460             :                              1,
     461             :                              subcredential,
     462             :                              hs_ntor_intro_cell_keys_out);
     463             : }
     464             : 
     465             : /**
     466             :  * As hs_ntor_service_get_introduce1_keys(), but take multiple subcredentials
     467             :  * as input, and yield multiple sets of keys as output.
     468             :  **/
     469             : int
     470           8 : hs_ntor_service_get_introduce1_keys_multi(
     471             :             const struct ed25519_public_key_t *intro_auth_pubkey,
     472             :             const struct curve25519_keypair_t *intro_enc_keypair,
     473             :             const struct curve25519_public_key_t *client_ephemeral_enc_pubkey,
     474             :             size_t n_subcredentials,
     475             :             const hs_subcredential_t *subcredentials,
     476             :             hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out)
     477             : {
     478           8 :   int bad = 0;
     479           8 :   uint8_t secret_input[INTRO_SECRET_HS_INPUT_LEN];
     480           8 :   uint8_t dh_result[CURVE25519_OUTPUT_LEN];
     481             : 
     482           8 :   tor_assert(intro_auth_pubkey);
     483           8 :   tor_assert(intro_enc_keypair);
     484           8 :   tor_assert(client_ephemeral_enc_pubkey);
     485           8 :   tor_assert(n_subcredentials >= 1);
     486           8 :   tor_assert(subcredentials);
     487           8 :   tor_assert(hs_ntor_intro_cell_keys_out);
     488             : 
     489             :   /* Compute EXP(X, b) */
     490           8 :   curve25519_handshake(dh_result,
     491             :                        &intro_enc_keypair->seckey,
     492             :                        client_ephemeral_enc_pubkey);
     493           8 :   bad |= safe_mem_is_zero(dh_result, CURVE25519_OUTPUT_LEN);
     494             : 
     495             :   /* Get intro_secret_hs_input */
     496           8 :   get_intro_secret_hs_input(dh_result, intro_auth_pubkey,
     497             :                             client_ephemeral_enc_pubkey,
     498             :                             &intro_enc_keypair->pubkey,
     499             :                             secret_input);
     500           8 :   bad |= safe_mem_is_zero(secret_input, CURVE25519_OUTPUT_LEN);
     501             : 
     502          36 :   for (unsigned i = 0; i < n_subcredentials; ++i) {
     503             :     /* Get ENC_KEY and MAC_KEY! */
     504          28 :     get_introduce1_key_material(secret_input, &subcredentials[i],
     505          28 :                                 &hs_ntor_intro_cell_keys_out[i]);
     506             :   }
     507             : 
     508           8 :   memwipe(secret_input,  0, sizeof(secret_input));
     509           8 :   if (bad) {
     510           1 :     memwipe(hs_ntor_intro_cell_keys_out, 0,
     511             :             sizeof(hs_ntor_intro_cell_keys_t) * n_subcredentials);
     512             :   }
     513             : 
     514           8 :   return bad ? -1 : 0;
     515             : }
     516             : 
     517             : /* Public function: Do the appropriate ntor calculations and derive the keys
     518             :  * needed to create and authenticate RENDEZVOUS1 cells. Return 0 and place the
     519             :  * final key material in <b>hs_ntor_rend_cell_keys_out</b> if all went fine,
     520             :  * return -1 if error happened.
     521             :  *
     522             :  * The relevant calculations are as follows:
     523             :  *
     524             :  *  rend_secret_hs_input = EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID
     525             :  *  NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc)
     526             :  *  verify = MAC(rend_secret_hs_input, t_hsverify)
     527             :  *  auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server"
     528             :  *  auth_input_mac = MAC(auth_input, t_hsmac)
     529             :  *
     530             :  * where:
     531             :  * <b>intro_auth_pubkey</b> is AUTH_KEY (intro point auth key),
     532             :  * <b>intro_enc_keypair</b> is (b,B) (intro point enc keypair)
     533             :  * <b>service_ephemeral_rend_keypair</b> is a fresh (y,Y) keypair
     534             :  * <b>client_ephemeral_enc_pubkey</b> is X (CLIENT_PK in INTRODUCE2 cell) */
     535             : int
     536           2 : hs_ntor_service_get_rendezvous1_keys(
     537             :                     const ed25519_public_key_t *intro_auth_pubkey,
     538             :                     const curve25519_keypair_t *intro_enc_keypair,
     539             :                     const curve25519_keypair_t *service_ephemeral_rend_keypair,
     540             :                     const curve25519_public_key_t *client_ephemeral_enc_pubkey,
     541             :                     hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out)
     542             : {
     543           2 :   int bad = 0;
     544           2 :   uint8_t rend_secret_hs_input[REND_SECRET_HS_INPUT_LEN];
     545           2 :   uint8_t dh_result1[CURVE25519_OUTPUT_LEN];
     546           2 :   uint8_t dh_result2[CURVE25519_OUTPUT_LEN];
     547             : 
     548           2 :   tor_assert(intro_auth_pubkey);
     549           2 :   tor_assert(intro_enc_keypair);
     550           2 :   tor_assert(service_ephemeral_rend_keypair);
     551           2 :   tor_assert(client_ephemeral_enc_pubkey);
     552           2 :   tor_assert(hs_ntor_rend_cell_keys_out);
     553             : 
     554             :   /* Compute EXP(X, y) */
     555           2 :   curve25519_handshake(dh_result1,
     556             :                        &service_ephemeral_rend_keypair->seckey,
     557             :                        client_ephemeral_enc_pubkey);
     558           2 :   bad |= safe_mem_is_zero(dh_result1, CURVE25519_OUTPUT_LEN);
     559             : 
     560             :   /* Compute EXP(X, b) */
     561           2 :   curve25519_handshake(dh_result2,
     562             :                        &intro_enc_keypair->seckey,
     563             :                        client_ephemeral_enc_pubkey);
     564           2 :   bad |= safe_mem_is_zero(dh_result2, CURVE25519_OUTPUT_LEN);
     565             : 
     566             :   /* Get rend_secret_hs_input */
     567           2 :   get_rend_secret_hs_input(dh_result1, dh_result2,
     568             :                            intro_auth_pubkey,
     569             :                            &intro_enc_keypair->pubkey,
     570             :                            client_ephemeral_enc_pubkey,
     571             :                            &service_ephemeral_rend_keypair->pubkey,
     572             :                            rend_secret_hs_input);
     573             : 
     574             :   /* Get NTOR_KEY_SEED and AUTH_INPUT_MAC! */
     575           2 :   bad |= get_rendezvous1_key_material(rend_secret_hs_input,
     576             :                                       intro_auth_pubkey,
     577             :                                       &intro_enc_keypair->pubkey,
     578             :                                       &service_ephemeral_rend_keypair->pubkey,
     579             :                                       client_ephemeral_enc_pubkey,
     580             :                                       hs_ntor_rend_cell_keys_out);
     581             : 
     582           2 :   memwipe(rend_secret_hs_input, 0, sizeof(rend_secret_hs_input));
     583           2 :   if (bad) {
     584           0 :     memwipe(hs_ntor_rend_cell_keys_out, 0, sizeof(hs_ntor_rend_cell_keys_t));
     585             :   }
     586             : 
     587           2 :   return bad ? -1 : 0;
     588             : }
     589             : 
     590             : /** Given a received RENDEZVOUS2 MAC in <b>mac</b> (of length DIGEST256_LEN),
     591             :  *  and the RENDEZVOUS1 key material in <b>hs_ntor_rend_cell_keys</b>, return 1
     592             :  *  if the MAC is good, otherwise return 0. */
     593             : int
     594           1 : hs_ntor_client_rendezvous2_mac_is_good(
     595             :                         const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys,
     596             :                         const uint8_t *rcvd_mac)
     597             : {
     598           1 :   tor_assert(rcvd_mac);
     599           1 :   tor_assert(hs_ntor_rend_cell_keys);
     600             : 
     601           1 :   return tor_memeq(hs_ntor_rend_cell_keys->rend_cell_auth_mac,
     602             :                    rcvd_mac, DIGEST256_LEN);
     603             : }
     604             : 
     605             : /* Input length to KDF for key expansion */
     606             : #define NTOR_KEY_EXPANSION_KDF_INPUT_LEN (DIGEST256_LEN + M_HSEXPAND_LEN)
     607             : 
     608             : /** Given the rendezvous key seed in <b>ntor_key_seed</b> (of size
     609             :  *  DIGEST256_LEN), do the circuit key expansion as specified by section
     610             :  *  '4.2.1. Key expansion' and place the keys in <b>keys_out</b> (which must be
     611             :  *  of size HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN).
     612             :  *
     613             :  * Return 0 if things went well, else return -1. */
     614             : int
     615           5 : hs_ntor_circuit_key_expansion(const uint8_t *ntor_key_seed, size_t seed_len,
     616             :                               uint8_t *keys_out, size_t keys_out_len)
     617             : {
     618           5 :   uint8_t *ptr;
     619           5 :   uint8_t kdf_input[NTOR_KEY_EXPANSION_KDF_INPUT_LEN];
     620             : 
     621             :   /* Sanity checks on lengths to make sure we are good */
     622           5 :   if (BUG(seed_len != DIGEST256_LEN)) {
     623           0 :     return -1;
     624             :   }
     625           5 :   if (BUG(keys_out_len != HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN)) {
     626           0 :     return -1;
     627             :   }
     628             : 
     629             :   /* Let's build the input to the KDF */
     630           5 :   ptr = kdf_input;
     631           5 :   APPEND(ptr, ntor_key_seed, DIGEST256_LEN);
     632           5 :   APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND));
     633           5 :   tor_assert(ptr == kdf_input + sizeof(kdf_input));
     634             : 
     635             :   /* Generate the keys */
     636           5 :   crypto_xof(keys_out, HS_NTOR_KEY_EXPANSION_KDF_OUT_LEN,
     637             :              kdf_input, sizeof(kdf_input));
     638             : 
     639           5 :   return 0;
     640             : }

Generated by: LCOV version 1.14