| 
					
				 | 
			
			
				@@ -0,0 +1,626 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* Copyright (c) 2017, The Tor Project, Inc. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* See LICENSE for licensing information */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** \file hs_ntor.c 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  \brief Implements the ntor variant used in Tor hidden services. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  \details 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  This module handles the variant of the ntor handshake that is documented in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  section [NTOR-WITH-EXTRA-DATA] of rend-spec-ng.txt . 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  The functions in this file provide an API that should be used when sending 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  or receiving INTRODUCE1/RENDEZVOUS1 cells to generate the various key 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  material required to create and handle those cells. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  In the case of INTRODUCE1 it provides encryption and MAC keys to 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  encode/decode the encrypted blob (see hs_ntor_intro_cell_keys_t). The 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  relevant pub functions are hs_ntor_{client,service}_get_introduce1_keys(). 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  In the case of RENDEZVOUS1 it calculates the MAC required to authenticate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  the cell, and also provides the key seed that is used to derive the crypto 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  material for rendezvous encryption (see hs_ntor_rend_cell_keys_t). The 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  relevant pub functions are hs_ntor_{client,service}_get_rendezvous1_keys(). 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  It also provides a function (hs_ntor_circuit_key_expansion()) that does the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  rendezvous key expansion to setup end-to-end rend circuit keys. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "or.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#include "hs_ntor.h" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* String constants used by the ntor HS protocol */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define PROTOID "tor-hs-ntor-curve25519-sha3-256-1" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define PROTOID_LEN (sizeof(PROTOID) - 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define SERVER_STR "Server" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define SERVER_STR_LEN (sizeof(SERVER_STR) - 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* Protocol-specific tweaks to our crypto inputs */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define T_HSENC PROTOID ":hs_key_extract" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define T_HSENC_LEN (sizeof(T_HSENC) - 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define T_HSVERIFY PROTOID ":hs_verify" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define T_HSMAC PROTOID ":hs_mac" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define M_HSEXPAND PROTOID ":hs_key_expand" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define M_HSEXPAND_LEN (sizeof(M_HSEXPAND) - 1) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/************************* Helper functions: *******************************/ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Helper macro: copy <b>len</b> bytes from <b>inp</b> to <b>ptr</b> and 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *advance <b>ptr</b> by the number of bytes copied. Stolen from onion_ntor.c */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define APPEND(ptr, inp, len)                   \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  STMT_BEGIN {                                  \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    memcpy(ptr, (inp), (len));                  \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    ptr += len;                                 \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } STMT_END 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* Length of EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define REND_SECRET_HS_INPUT_LEN (CURVE25519_OUTPUT_LEN * 2 + \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ED25519_PUBKEY_LEN + CURVE25519_PUBKEY_LEN * 3 + PROTOID_LEN) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* Length of auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server" */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define REND_AUTH_INPUT_LEN (DIGEST256_LEN + ED25519_PUBKEY_LEN + \ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CURVE25519_PUBKEY_LEN * 3 + PROTOID_LEN + SERVER_STR_LEN) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Helper function: Compute the last part of the HS ntor handshake which 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  derives key material necessary to create and handle RENDEZVOUS1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  cells. Function used by both client and service. The actual calculations is 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  as follows: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *    NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *    verify = MAC(rend_secret_hs_input, t_hsverify) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *    auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *    auth_input_mac = MAC(auth_input, t_hsmac) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  where in the above, AUTH_KEY is <b>intro_auth_pubkey</b>, B is 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  <b>intro_enc_pubkey</b>, Y is <b>service_ephemeral_rend_pubkey</b>, and X 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  is <b>client_ephemeral_enc_pubkey</b>. The provided 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  <b>rend_secret_hs_input</b> is of size REND_SECRET_HS_INPUT_LEN. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  The final results of NTOR_KEY_SEED and auth_input_mac are placed in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  <b>hs_ntor_rend_cell_keys_out</b>. Return 0 if everything went fine. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+get_rendezvous1_key_material(const uint8_t *rend_secret_hs_input, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  const ed25519_public_key_t *intro_auth_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  const curve25519_public_key_t *intro_enc_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  const curve25519_public_key_t *service_ephemeral_rend_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  const curve25519_public_key_t *client_ephemeral_enc_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int bad = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t ntor_key_seed[DIGEST256_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t ntor_verify[DIGEST256_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t rend_auth_input[REND_AUTH_INPUT_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t rend_cell_auth[DIGEST256_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t *ptr; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Let's build NTOR_KEY_SEED */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  crypto_mac_sha3_256(ntor_key_seed, sizeof(ntor_key_seed), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      rend_secret_hs_input, REND_SECRET_HS_INPUT_LEN, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      (const uint8_t *)T_HSENC, strlen(T_HSENC)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bad |= safe_mem_is_zero(ntor_key_seed, DIGEST256_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Let's build ntor_verify */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  crypto_mac_sha3_256(ntor_verify, sizeof(ntor_verify), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      rend_secret_hs_input, REND_SECRET_HS_INPUT_LEN, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      (const uint8_t *)T_HSVERIFY, strlen(T_HSVERIFY)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bad |= safe_mem_is_zero(ntor_verify, DIGEST256_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Let's build auth_input: */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ptr = rend_auth_input; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Append ntor_verify */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, ntor_verify, sizeof(ntor_verify)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Append AUTH_KEY */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, intro_auth_pubkey->pubkey, ED25519_PUBKEY_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Append B */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, intro_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Append Y */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         service_ephemeral_rend_pubkey->public_key, CURVE25519_PUBKEY_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Append X */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         client_ephemeral_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Append PROTOID */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, PROTOID, strlen(PROTOID)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Append "Server" */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, SERVER_STR, strlen(SERVER_STR)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(ptr == rend_auth_input + sizeof(rend_auth_input)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Let's build auth_input_mac that goes in RENDEZVOUS1 cell */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  crypto_mac_sha3_256(rend_cell_auth, sizeof(rend_cell_auth), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      rend_auth_input, sizeof(rend_auth_input), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      (const uint8_t *)T_HSMAC, strlen(T_HSMAC)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bad |= safe_mem_is_zero(ntor_verify, DIGEST256_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  { /* Get the computed RENDEZVOUS1 material! */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    memcpy(&hs_ntor_rend_cell_keys_out->rend_cell_auth_mac, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+           rend_cell_auth, DIGEST256_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    memcpy(&hs_ntor_rend_cell_keys_out->ntor_key_seed, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+           ntor_key_seed, DIGEST256_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  memwipe(rend_cell_auth, 0, sizeof(rend_cell_auth)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  memwipe(rend_auth_input, 0, sizeof(rend_auth_input)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  memwipe(ntor_key_seed, 0, sizeof(ntor_key_seed)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return bad; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Length of secret_input = EXP(B,x) | AUTH_KEY | X | B | PROTOID */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define INTRO_SECRET_HS_INPUT_LEN (CURVE25519_OUTPUT_LEN +ED25519_PUBKEY_LEN +\ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  CURVE25519_PUBKEY_LEN + CURVE25519_PUBKEY_LEN + PROTOID_LEN) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* Length of info = m_hsexpand | subcredential */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define INFO_BLOB_LEN (M_HSEXPAND_LEN + DIGEST256_LEN) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* Length of KDF input = intro_secret_hs_input | t_hsenc | info */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define KDF_INPUT_LEN (INTRO_SECRET_HS_INPUT_LEN + T_HSENC_LEN + INFO_BLOB_LEN) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Helper function: Compute the part of the HS ntor handshake that generates 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  key material for creating and handling INTRODUCE1 cells. Function used 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  by both client and service. Specifically, calculate the following: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *     info = m_hsexpand | subcredential 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *     hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *     ENC_KEY = hs_keys[0:S_KEY_LEN] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *     MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  where intro_secret_hs_input is <b>secret_input</b> (of size 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  INTRO_SECRET_HS_INPUT_LEN), and <b>subcredential</b> is of size 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  DIGEST256_LEN. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * If everything went well, fill <b>hs_ntor_intro_cell_keys_out</b> with the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * necessary key material, and return 0. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+get_introduce1_key_material(const uint8_t *secret_input, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        const uint8_t *subcredential, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t keystream[CIPHER256_KEY_LEN + DIGEST256_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t info_blob[INFO_BLOB_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t kdf_input[KDF_INPUT_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  crypto_xof_t *xof; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t *ptr; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Let's build info */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ptr = info_blob; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, subcredential, DIGEST256_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(ptr == info_blob + sizeof(info_blob)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Let's build the input to the KDF */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ptr = kdf_input; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, secret_input, INTRO_SECRET_HS_INPUT_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, T_HSENC, strlen(T_HSENC)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, info_blob, sizeof(info_blob)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(ptr == kdf_input + sizeof(kdf_input)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Now we need to run kdf_input over SHAKE-256 */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  xof = crypto_xof_new(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  crypto_xof_add_bytes(xof, kdf_input, sizeof(kdf_input)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  crypto_xof_squeeze_bytes(xof, keystream, sizeof(keystream)) ; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  crypto_xof_free(xof); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  { /* Get the keys */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    memcpy(&hs_ntor_intro_cell_keys_out->enc_key, keystream,CIPHER256_KEY_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    memcpy(&hs_ntor_intro_cell_keys_out->mac_key, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+           keystream+CIPHER256_KEY_LEN, DIGEST256_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  memwipe(keystream,  0, sizeof(keystream)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  memwipe(kdf_input,  0, sizeof(kdf_input)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Helper function: Calculate the 'intro_secret_hs_input' element used by the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * HS ntor handshake and place it in <b>secret_input_out</b>. This function is 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * used by both client and service code. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * For the client-side it looks like this: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *         intro_secret_hs_input = EXP(B,x) | AUTH_KEY | X | B | PROTOID 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * whereas for the service-side it looks like this: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *         intro_secret_hs_input = EXP(X,b) | AUTH_KEY | X | B | PROTOID 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * In this function, <b>dh_result</b> carries the EXP() result (and has size 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * CURVE25519_OUTPUT_LEN) <b>intro_auth_pubkey</b> is AUTH_KEY, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>client_ephemeral_enc_pubkey</b> is X, and <b>intro_enc_pubkey</b> is B. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+get_intro_secret_hs_input(const uint8_t *dh_result, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    const ed25519_public_key_t *intro_auth_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    const curve25519_public_key_t *client_ephemeral_enc_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    const curve25519_public_key_t *intro_enc_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    uint8_t *secret_input_out) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t *ptr; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Append EXP() */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ptr = secret_input_out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, dh_result, CURVE25519_OUTPUT_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Append AUTH_KEY */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, intro_auth_pubkey->pubkey, ED25519_PUBKEY_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Append X */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, client_ephemeral_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Append B */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, intro_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Append PROTOID */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, PROTOID, strlen(PROTOID)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(ptr == secret_input_out + INTRO_SECRET_HS_INPUT_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Calculate the 'rend_secret_hs_input' element used by the HS ntor handshake 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  and place it in <b>rend_secret_hs_input_out</b>. This function is used by 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  both client and service code. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * The computation on the client side is: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  rend_secret_hs_input = EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * whereas on the service side it is: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  rend_secret_hs_input = EXP(Y,x) | EXP(B,x) | AUTH_KEY | B | X | Y | PROTOID 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * where: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>dh_result1</b> and <b>dh_result2</b> carry the two EXP() results (of size 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * CURVE25519_OUTPUT_LEN) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>intro_auth_pubkey</b> is AUTH_KEY, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>intro_enc_pubkey</b> is B, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>client_ephemeral_enc_pubkey</b> is X, and 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>service_ephemeral_rend_pubkey</b> is Y. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+static void 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+get_rend_secret_hs_input(const uint8_t *dh_result1, const uint8_t *dh_result2, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  const ed25519_public_key_t *intro_auth_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  const curve25519_public_key_t *intro_enc_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  const curve25519_public_key_t *client_ephemeral_enc_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  const curve25519_public_key_t *service_ephemeral_rend_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  uint8_t *rend_secret_hs_input_out) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t *ptr; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ptr = rend_secret_hs_input_out; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Append the first EXP() */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, dh_result1, CURVE25519_OUTPUT_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Append the other EXP() */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, dh_result2, CURVE25519_OUTPUT_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Append AUTH_KEY */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, intro_auth_pubkey->pubkey, ED25519_PUBKEY_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Append B */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, intro_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Append X */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         client_ephemeral_enc_pubkey->public_key, CURVE25519_PUBKEY_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Append Y */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+         service_ephemeral_rend_pubkey->public_key, CURVE25519_PUBKEY_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Append PROTOID */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, PROTOID, strlen(PROTOID)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(ptr == rend_secret_hs_input_out + REND_SECRET_HS_INPUT_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/************************* Public functions: *******************************/ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* Public function: Do the appropriate ntor calculations and derive the keys 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * needed to encrypt and authenticate INTRODUCE1 cells. Return 0 and place the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * final key material in <b>hs_ntor_intro_cell_keys_out</b> if everything went 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * well, otherwise return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * The relevant calculations are as follows: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *     intro_secret_hs_input = EXP(B,x) | AUTH_KEY | X | B | PROTOID 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *     info = m_hsexpand | subcredential 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *     hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *     ENC_KEY = hs_keys[0:S_KEY_LEN] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *     MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * where: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>intro_auth_pubkey</b> is AUTH_KEY (found in HS descriptor), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>intro_enc_pubkey</b> is B (also found in HS descriptor), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>client_ephemeral_enc_keypair</b> is freshly generated keypair (x,X) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>subcredential</b> is the hidden service subcredential (of size 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * DIGEST256_LEN). */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+hs_ntor_client_get_introduce1_keys( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      const ed25519_public_key_t *intro_auth_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      const curve25519_public_key_t *intro_enc_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      const curve25519_keypair_t *client_ephemeral_enc_keypair, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      const uint8_t *subcredential, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                      hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int bad = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t secret_input[INTRO_SECRET_HS_INPUT_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t dh_result[CURVE25519_OUTPUT_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(intro_auth_pubkey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(intro_enc_pubkey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(client_ephemeral_enc_keypair); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(subcredential); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(hs_ntor_intro_cell_keys_out); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Calculate EXP(B,x) */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  curve25519_handshake(dh_result, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       &client_ephemeral_enc_keypair->seckey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       intro_enc_pubkey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bad |= safe_mem_is_zero(dh_result, CURVE25519_OUTPUT_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Get intro_secret_hs_input */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  get_intro_secret_hs_input(dh_result, intro_auth_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            &client_ephemeral_enc_keypair->pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            intro_enc_pubkey, secret_input); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bad |= safe_mem_is_zero(secret_input, CURVE25519_OUTPUT_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Get ENC_KEY and MAC_KEY! */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  get_introduce1_key_material(secret_input, subcredential, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                              hs_ntor_intro_cell_keys_out); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Cleanup */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  memwipe(secret_input,  0, sizeof(secret_input)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (bad) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    memwipe(hs_ntor_intro_cell_keys_out, 0, sizeof(hs_ntor_intro_cell_keys_t)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return bad ? -1 : 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* Public function: Do the appropriate ntor calculations and derive the keys 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * needed to verify RENDEZVOUS1 cells and encrypt further rendezvous 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * traffic. Return 0 and place the final key material in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>hs_ntor_rend_cell_keys_out</b> if everything went well, else return -1. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * The relevant calculations are as follows: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  rend_secret_hs_input = EXP(Y,x) | EXP(B,x) | AUTH_KEY | B | X | Y | PROTOID 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  verify = MAC(rend_secret_hs_input, t_hsverify) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  auth_input_mac = MAC(auth_input, t_hsmac) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * where: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>intro_auth_pubkey</b> is AUTH_KEY (found in HS descriptor), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>client_ephemeral_enc_keypair</b> is freshly generated keypair (x,X) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>intro_enc_pubkey</b> is B (also found in HS descriptor), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>service_ephemeral_rend_pubkey</b> is Y (SERVER_PK in RENDEZVOUS1 cell) */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+hs_ntor_client_get_rendezvous1_keys( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  const ed25519_public_key_t *intro_auth_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  const curve25519_keypair_t *client_ephemeral_enc_keypair, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  const curve25519_public_key_t *intro_enc_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  const curve25519_public_key_t *service_ephemeral_rend_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                  hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int bad = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t rend_secret_hs_input[REND_SECRET_HS_INPUT_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t dh_result1[CURVE25519_OUTPUT_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t dh_result2[CURVE25519_OUTPUT_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(intro_auth_pubkey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(client_ephemeral_enc_keypair); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(intro_enc_pubkey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(service_ephemeral_rend_pubkey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(hs_ntor_rend_cell_keys_out); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Compute EXP(Y, x) */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  curve25519_handshake(dh_result1, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       &client_ephemeral_enc_keypair->seckey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       service_ephemeral_rend_pubkey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bad |= safe_mem_is_zero(dh_result1, CURVE25519_OUTPUT_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Compute EXP(B, x) */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  curve25519_handshake(dh_result2, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       &client_ephemeral_enc_keypair->seckey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       intro_enc_pubkey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bad |= safe_mem_is_zero(dh_result2, CURVE25519_OUTPUT_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Get rend_secret_hs_input */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  get_rend_secret_hs_input(dh_result1, dh_result2, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           intro_auth_pubkey, intro_enc_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           &client_ephemeral_enc_keypair->pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           service_ephemeral_rend_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           rend_secret_hs_input); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Get NTOR_KEY_SEED and the auth_input MAC */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bad |= get_rendezvous1_key_material(rend_secret_hs_input, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      intro_auth_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      intro_enc_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      service_ephemeral_rend_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      &client_ephemeral_enc_keypair->pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      hs_ntor_rend_cell_keys_out); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  memwipe(rend_secret_hs_input, 0, sizeof(rend_secret_hs_input)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (bad) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    memwipe(hs_ntor_rend_cell_keys_out, 0, sizeof(hs_ntor_rend_cell_keys_t)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return bad ? -1 : 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* Public function: Do the appropriate ntor calculations and derive the keys 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * needed to decrypt and verify INTRODUCE1 cells. Return 0 and place the final 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * key material in <b>hs_ntor_intro_cell_keys_out</b> if everything went well, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * otherwise return -1; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * The relevant calculations are as follows: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *    intro_secret_hs_input = EXP(X,b) | AUTH_KEY | X | B | PROTOID 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *    info = m_hsexpand | subcredential 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *    hs_keys = KDF(intro_secret_hs_input | t_hsenc | info, S_KEY_LEN+MAC_LEN) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *    HS_DEC_KEY = hs_keys[0:S_KEY_LEN] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *    HS_MAC_KEY = hs_keys[S_KEY_LEN:S_KEY_LEN+MAC_KEY_LEN] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * where: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>intro_auth_pubkey</b> is AUTH_KEY (introduction point auth key), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>intro_enc_keypair</b> is (b,B) (introduction point encryption keypair), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>client_ephemeral_enc_pubkey</b> is X (CLIENT_PK in INTRODUCE2 cell), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>subcredential</b> is the HS subcredential (of size DIGEST256_LEN) */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+hs_ntor_service_get_introduce1_keys( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    const ed25519_public_key_t *intro_auth_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    const curve25519_keypair_t *intro_enc_keypair, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    const curve25519_public_key_t *client_ephemeral_enc_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    const uint8_t *subcredential, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    hs_ntor_intro_cell_keys_t *hs_ntor_intro_cell_keys_out) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int bad = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t secret_input[INTRO_SECRET_HS_INPUT_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t dh_result[CURVE25519_OUTPUT_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(intro_auth_pubkey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(intro_enc_keypair); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(client_ephemeral_enc_pubkey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(subcredential); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(hs_ntor_intro_cell_keys_out); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Compute EXP(X, b) */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  curve25519_handshake(dh_result, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       &intro_enc_keypair->seckey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       client_ephemeral_enc_pubkey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bad |= safe_mem_is_zero(dh_result, CURVE25519_OUTPUT_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Get intro_secret_hs_input */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  get_intro_secret_hs_input(dh_result, intro_auth_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            client_ephemeral_enc_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            &intro_enc_keypair->pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            secret_input); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bad |= safe_mem_is_zero(secret_input, CURVE25519_OUTPUT_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Get ENC_KEY and MAC_KEY! */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  get_introduce1_key_material(secret_input, subcredential, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                              hs_ntor_intro_cell_keys_out); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  memwipe(secret_input,  0, sizeof(secret_input)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (bad) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    memwipe(hs_ntor_intro_cell_keys_out, 0, sizeof(hs_ntor_intro_cell_keys_t)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return bad ? -1 : 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* Public function: Do the appropriate ntor calculations and derive the keys 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * needed to create and authenticate RENDEZVOUS1 cells. Return 0 and place the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * final key material in <b>hs_ntor_rend_cell_keys_out</b> if all went fine, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * return -1 if error happened. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * The relevant calculations are as follows: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  rend_secret_hs_input = EXP(X,y) | EXP(X,b) | AUTH_KEY | B | X | Y | PROTOID 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  NTOR_KEY_SEED = MAC(rend_secret_hs_input, t_hsenc) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  verify = MAC(rend_secret_hs_input, t_hsverify) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  auth_input = verify | AUTH_KEY | B | Y | X | PROTOID | "Server" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  auth_input_mac = MAC(auth_input, t_hsmac) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * where: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>intro_auth_pubkey</b> is AUTH_KEY (intro point auth key), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>intro_enc_keypair</b> is (b,B) (intro point enc keypair) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>service_ephemeral_rend_keypair</b> is a fresh (y,Y) keypair 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ * <b>client_ephemeral_enc_pubkey</b> is X (CLIENT_PK in INTRODUCE2 cell) */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+hs_ntor_service_get_rendezvous1_keys( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    const ed25519_public_key_t *intro_auth_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    const curve25519_keypair_t *intro_enc_keypair, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    const curve25519_keypair_t *service_ephemeral_rend_keypair, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    const curve25519_public_key_t *client_ephemeral_enc_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys_out) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  int bad = 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t rend_secret_hs_input[REND_SECRET_HS_INPUT_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t dh_result1[CURVE25519_OUTPUT_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t dh_result2[CURVE25519_OUTPUT_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(intro_auth_pubkey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(intro_enc_keypair); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(service_ephemeral_rend_keypair); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(client_ephemeral_enc_pubkey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(hs_ntor_rend_cell_keys_out); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Compute EXP(X, y) */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  curve25519_handshake(dh_result1, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       &service_ephemeral_rend_keypair->seckey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       client_ephemeral_enc_pubkey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bad |= safe_mem_is_zero(dh_result1, CURVE25519_OUTPUT_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Compute EXP(X, b) */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  curve25519_handshake(dh_result2, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       &intro_enc_keypair->seckey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       client_ephemeral_enc_pubkey); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bad |= safe_mem_is_zero(dh_result2, CURVE25519_OUTPUT_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Get rend_secret_hs_input */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  get_rend_secret_hs_input(dh_result1, dh_result2, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           intro_auth_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           &intro_enc_keypair->pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           client_ephemeral_enc_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           &service_ephemeral_rend_keypair->pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                           rend_secret_hs_input); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Get NTOR_KEY_SEED and AUTH_INPUT_MAC! */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  bad |= get_rendezvous1_key_material(rend_secret_hs_input, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      intro_auth_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      &intro_enc_keypair->pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      &service_ephemeral_rend_keypair->pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      client_ephemeral_enc_pubkey, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                                      hs_ntor_rend_cell_keys_out); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  memwipe(rend_secret_hs_input, 0, sizeof(rend_secret_hs_input)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  if (bad) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    memwipe(hs_ntor_rend_cell_keys_out, 0, sizeof(hs_ntor_rend_cell_keys_t)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return bad ? -1 : 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Given a received RENDEZVOUS2 MAC in <b>mac</b> (of length DIGEST256_LEN), 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  and the RENDEZVOUS1 key material in <b>hs_ntor_rend_cell_keys</b>, return 1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  if the MAC is good, otherwise return 0. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+int 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+hs_ntor_client_rendezvous2_mac_is_good( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        const uint8_t *rcvd_mac) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(rcvd_mac); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(hs_ntor_rend_cell_keys); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return tor_memeq(hs_ntor_rend_cell_keys->rend_cell_auth_mac, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                   rcvd_mac, DIGEST256_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* Input length to KDF for key expansion */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define NTOR_KEY_EXPANSION_KDF_INPUT_LEN (DIGEST256_LEN + M_HSEXPAND_LEN) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/* Output length of KDF for key expansion */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+#define NTOR_KEY_EXPANSION_KDF_OUTPUT_LEN (DIGEST256_LEN*3+CIPHER256_KEY_LEN*2) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+/** Given the rendezvous key material in <b>hs_ntor_rend_cell_keys</b>, do the 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  circuit key expansion as specified by section '4.2.1. Key expansion' and 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ *  return a hs_ntor_rend_circuit_keys_t structure with the computed keys. */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+hs_ntor_rend_circuit_keys_t * 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+hs_ntor_circuit_key_expansion( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                       const hs_ntor_rend_cell_keys_t *hs_ntor_rend_cell_keys) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t *ptr; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t kdf_input[NTOR_KEY_EXPANSION_KDF_INPUT_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  uint8_t keys[NTOR_KEY_EXPANSION_KDF_OUTPUT_LEN]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  crypto_xof_t *xof; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  hs_ntor_rend_circuit_keys_t *rend_circuit_keys = NULL; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Let's build the input to the KDF */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ptr = kdf_input; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, hs_ntor_rend_cell_keys->ntor_key_seed, DIGEST256_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  APPEND(ptr, M_HSEXPAND, strlen(M_HSEXPAND)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(ptr == kdf_input + sizeof(kdf_input)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Generate the keys */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  xof = crypto_xof_new(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  crypto_xof_add_bytes(xof, kdf_input, sizeof(kdf_input)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  crypto_xof_squeeze_bytes(xof, keys, sizeof(keys)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  crypto_xof_free(xof); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  /* Generate keys structure and assign keys to it */ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  rend_circuit_keys = tor_malloc_zero(sizeof(hs_ntor_rend_circuit_keys_t)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ptr = keys; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  memcpy(rend_circuit_keys->KH, ptr, DIGEST256_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ptr += DIGEST256_LEN;; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  memcpy(rend_circuit_keys->Df, ptr, DIGEST256_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ptr += DIGEST256_LEN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  memcpy(rend_circuit_keys->Db, ptr, DIGEST256_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ptr += DIGEST256_LEN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  memcpy(rend_circuit_keys->Kf, ptr, CIPHER256_KEY_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ptr += CIPHER256_KEY_LEN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  memcpy(rend_circuit_keys->Kb, ptr, CIPHER256_KEY_LEN); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  ptr += CIPHER256_KEY_LEN; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  tor_assert(ptr == keys + sizeof(keys)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+  return rend_circuit_keys; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+} 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 |