test_hs_ob.c (7760B)
1 /* Copyright (c) 2020-2021, The Tor Project, Inc. */ 2 /* See LICENSE for licensing information */ 3 4 /** 5 * \file test_hs_ob.c 6 * \brief Test hidden service onion balance functionality. 7 */ 8 9 #define CONFIG_PRIVATE 10 #define HS_SERVICE_PRIVATE 11 #define HS_OB_PRIVATE 12 13 #include "test/test.h" 14 #include "test/test_helpers.h" 15 #include "test/log_test_helpers.h" 16 17 #include "app/config/config.h" 18 #include "feature/hs/hs_config.h" 19 #include "feature/hs/hs_ob.h" 20 #include "feature/hs/hs_service.h" 21 #include "feature/nodelist/networkstatus.h" 22 #include "feature/nodelist/networkstatus_st.h" 23 24 static ed25519_keypair_t onion_addr_kp_1; 25 static char onion_addr_1[HS_SERVICE_ADDR_LEN_BASE32 + 1]; 26 27 static ed25519_keypair_t onion_addr_kp_2; 28 static char onion_addr_2[HS_SERVICE_ADDR_LEN_BASE32 + 1]; 29 30 static bool config_is_good = true; 31 32 static int 33 helper_tor_config(const char *conf) 34 { 35 int ret = -1; 36 or_options_t *options = helper_parse_options(conf); 37 tt_assert(options); 38 ret = hs_config_service_all(options, 0); 39 done: 40 or_options_free(options); 41 return ret; 42 } 43 44 static networkstatus_t mock_ns; 45 46 static networkstatus_t * 47 mock_networkstatus_get_live_consensus(time_t now) 48 { 49 (void) now; 50 return &mock_ns; 51 } 52 53 static char * 54 mock_read_file_to_str(const char *filename, int flags, struct stat *stat_out) 55 { 56 char *ret = NULL; 57 58 (void) flags; 59 (void) stat_out; 60 61 if (!strcmp(filename, get_fname("hs3" PATH_SEPARATOR "ob_config"))) { 62 if (config_is_good) { 63 tor_asprintf(&ret, "MasterOnionAddress %s.onion\n" 64 "MasterOnionAddress %s.onion\n", 65 onion_addr_1, onion_addr_2); 66 } else { 67 tor_asprintf(&ret, "MasterOnionAddress JUNKJUNKJUNK.onion\n" 68 "UnknownOption BLAH\n"); 69 } 70 goto done; 71 } 72 73 done: 74 return ret; 75 } 76 77 static void 78 test_parse_config_file(void *arg) 79 { 80 int ret; 81 char *conf = NULL; 82 const ed25519_public_key_t *pkey; 83 84 (void) arg; 85 86 hs_init(); 87 88 MOCK(read_file_to_str, mock_read_file_to_str); 89 90 #define fmt_conf \ 91 "HiddenServiceDir %s\n" \ 92 "HiddenServicePort 22\n" \ 93 "HiddenServiceOnionBalanceInstance 1\n" 94 tor_asprintf(&conf, fmt_conf, get_fname("hs3")); 95 #undef fmt_conf 96 97 /* Build the OB frontend onion addresses. */ 98 ed25519_keypair_generate(&onion_addr_kp_1, 0); 99 hs_build_address(&onion_addr_kp_1.pubkey, HS_VERSION_THREE, onion_addr_1); 100 ed25519_keypair_generate(&onion_addr_kp_2, 0); 101 hs_build_address(&onion_addr_kp_2.pubkey, HS_VERSION_THREE, onion_addr_2); 102 103 ret = helper_tor_config(conf); 104 tor_free(conf); 105 tt_int_op(ret, OP_EQ, 0); 106 107 /* Load the keys for the service. After that, the v3 service should be 108 * registered in the global map and we'll be able to access it. */ 109 tt_int_op(get_hs_service_staging_list_size(), OP_EQ, 1); 110 hs_service_load_all_keys(); 111 tt_int_op(get_hs_service_map_size(), OP_EQ, 1); 112 const hs_service_t *s = get_first_service(); 113 tt_assert(s); 114 tt_assert(s->config.ob_master_pubkeys); 115 tt_assert(hs_ob_service_is_instance(s)); 116 tt_assert(smartlist_len(s->config.ob_master_pubkeys) == 2); 117 118 /* Test the public keys we've added. */ 119 pkey = smartlist_get(s->config.ob_master_pubkeys, 0); 120 tt_mem_op(&onion_addr_kp_1.pubkey, OP_EQ, pkey, ED25519_PUBKEY_LEN); 121 pkey = smartlist_get(s->config.ob_master_pubkeys, 1); 122 tt_mem_op(&onion_addr_kp_2.pubkey, OP_EQ, pkey, ED25519_PUBKEY_LEN); 123 124 done: 125 hs_free_all(); 126 127 UNMOCK(read_file_to_str); 128 } 129 130 static void 131 test_parse_config_file_bad(void *arg) 132 { 133 int ret; 134 char *conf = NULL; 135 136 (void) arg; 137 138 hs_init(); 139 140 MOCK(read_file_to_str, mock_read_file_to_str); 141 142 /* Indicate mock_read_file_to_str() to use the bad config. */ 143 config_is_good = false; 144 145 #define fmt_conf \ 146 "HiddenServiceDir %s\n" \ 147 "HiddenServicePort 22\n" \ 148 "HiddenServiceOnionBalanceInstance 1\n" 149 tor_asprintf(&conf, fmt_conf, get_fname("hs3")); 150 #undef fmt_conf 151 152 setup_full_capture_of_logs(LOG_INFO); 153 ret = helper_tor_config(conf); 154 tor_free(conf); 155 tt_int_op(ret, OP_EQ, -1); 156 expect_log_msg_containing("OnionBalance: MasterOnionAddress " 157 "JUNKJUNKJUNK.onion is invalid"); 158 expect_log_msg_containing("Found unrecognized option \'UnknownOption\'; " 159 "saving it."); 160 teardown_capture_of_logs(); 161 162 done: 163 hs_free_all(); 164 165 UNMOCK(read_file_to_str); 166 } 167 168 static void 169 test_get_subcredentials(void *arg) 170 { 171 int ret; 172 hs_service_t *service = NULL; 173 hs_service_config_t config; 174 hs_subcredential_t *subcreds = NULL; 175 176 (void) arg; 177 memset(&config, 0, sizeof(config)); 178 179 MOCK(networkstatus_get_live_consensus, 180 mock_networkstatus_get_live_consensus); 181 182 /* Setup consensus with proper time so we can compute the time period. */ 183 ret = parse_rfc1123_time("Sat, 26 Oct 1985 13:00:00 UTC", 184 &mock_ns.valid_after); 185 tt_int_op(ret, OP_EQ, 0); 186 ret = parse_rfc1123_time("Sat, 26 Oct 1985 14:00:00 UTC", 187 &mock_ns.fresh_until); 188 tt_int_op(ret, OP_EQ, 0); 189 190 config.ob_master_pubkeys = smartlist_new(); 191 tt_assert(config.ob_master_pubkeys); 192 193 /* Set up an instance */ 194 service = tor_malloc_zero(sizeof(hs_service_t)); 195 service->config = config; 196 /* Setup the service descriptors */ 197 service->desc_current = service_descriptor_new(); 198 service->desc_next = service_descriptor_new(); 199 200 /* First try to compute subcredentials but with no OB keys. Make sure that 201 * subcreds get NULLed. To do this check we first poison subcreds. */ 202 subcreds = (void*)999; 203 tt_ptr_op(subcreds, OP_NE, NULL); 204 size_t num = compute_subcredentials(service, &subcreds); 205 tt_ptr_op(subcreds, OP_EQ, NULL); 206 207 /* Generate a keypair to add to the OB keys list. */ 208 ed25519_keypair_generate(&onion_addr_kp_1, 0); 209 smartlist_add(config.ob_master_pubkeys, &onion_addr_kp_1.pubkey); 210 211 /* Set up the instance subcredentials */ 212 char current_subcred[SUBCRED_LEN]; 213 char next_subcred[SUBCRED_LEN]; 214 memset(current_subcred, 'C', SUBCRED_LEN); 215 memset(next_subcred, 'N', SUBCRED_LEN); 216 memcpy(service->desc_current->desc->subcredential.subcred, current_subcred, 217 SUBCRED_LEN); 218 memcpy(service->desc_next->desc->subcredential.subcred, next_subcred, 219 SUBCRED_LEN); 220 221 /* See that subcreds are computed properly */ 222 num = compute_subcredentials(service, &subcreds); 223 /* 5 subcredentials: 3 for the frontend, 2 for the instance */ 224 tt_uint_op(num, OP_EQ, 5); 225 tt_ptr_op(subcreds, OP_NE, NULL); 226 227 /* Validate the subcredentials we just got. We'll build them oursevles with 228 * the right time period steps and compare. */ 229 const uint64_t tp = hs_get_time_period_num(0); 230 const int steps[3] = {0, -1, 1}; 231 232 unsigned int i; 233 for (i = 0; i < 3; i++) { 234 hs_subcredential_t subcredential; 235 ed25519_public_key_t blinded_pubkey; 236 hs_build_blinded_pubkey(&onion_addr_kp_1.pubkey, NULL, 0, tp + steps[i], 237 &blinded_pubkey); 238 hs_get_subcredential(&onion_addr_kp_1.pubkey, &blinded_pubkey, 239 &subcredential); 240 tt_mem_op(subcreds[i].subcred, OP_EQ, subcredential.subcred, 241 SUBCRED_LEN); 242 } 243 244 tt_mem_op(subcreds[i++].subcred, OP_EQ, current_subcred, SUBCRED_LEN); 245 tt_mem_op(subcreds[i++].subcred, OP_EQ, next_subcred, SUBCRED_LEN); 246 247 done: 248 tor_free(subcreds); 249 250 smartlist_free(config.ob_master_pubkeys); 251 if (service) { 252 memset(&service->config, 0, sizeof(hs_service_config_t)); 253 hs_service_free(service); 254 } 255 256 UNMOCK(networkstatus_get_live_consensus); 257 } 258 259 struct testcase_t hs_ob_tests[] = { 260 { "parse_config_file", test_parse_config_file, TT_FORK, 261 NULL, NULL }, 262 { "parse_config_file_bad", test_parse_config_file_bad, TT_FORK, 263 NULL, NULL }, 264 265 { "get_subcredentials", test_get_subcredentials, TT_FORK, 266 NULL, NULL }, 267 268 END_OF_TESTCASES 269 };