test_hs_pow.c (17709B)
1 /* Copyright (c) 2020-2023, The Tor Project, Inc. */ 2 /* See LICENSE for licensing information */ 3 4 /** 5 * \file test_hs_pow.c 6 * \brief Tests for service proof-of-work verification and wire protocol. 7 */ 8 9 #define HS_SERVICE_PRIVATE 10 #define HS_CIRCUIT_PRIVATE 11 12 #include "lib/cc/compat_compiler.h" 13 #include "lib/cc/torint.h" 14 15 #include "test/hs_test_helpers.h" 16 #include "test/log_test_helpers.h" 17 #include "test/test_helpers.h" 18 #include "test/test.h" 19 20 #include "app/config/config.h" 21 #include "core/or/circuitbuild.h" 22 #include "core/or/circuitlist.h" 23 #include "core/or/relay.h" 24 #include "feature/hs/hs_cell.h" 25 #include "feature/hs/hs_circuit.h" 26 #include "feature/hs/hs_metrics.h" 27 #include "feature/hs/hs_pow.h" 28 #include "feature/hs/hs_service.h" 29 #include "feature/nodelist/nodelist.h" 30 31 #include "core/or/crypt_path_st.h" 32 #include "core/or/origin_circuit_st.h" 33 #include "feature/nodelist/node_st.h" 34 #include "feature/nodelist/routerinfo_st.h" 35 36 #include "trunnel/hs/cell_introduce1.h" 37 38 static int test_rend_launch_count; 39 static uint32_t test_rend_launch_expect_effort; 40 41 static void 42 mock_launch_rendezvous_point_circuit(const hs_service_t *service, 43 const ed25519_public_key_t *ip_auth_pubkey, 44 const curve25519_keypair_t *ip_enc_key_kp, 45 const hs_cell_intro_rdv_data_t *rdv_data, 46 time_t now) 47 { 48 (void) service; 49 (void) ip_auth_pubkey; 50 (void) ip_enc_key_kp; 51 (void) rdv_data; 52 (void) now; 53 54 tt_int_op(test_rend_launch_expect_effort, OP_EQ, rdv_data->pow_effort); 55 test_rend_launch_count++; 56 57 done: 58 ; 59 } 60 61 static node_t *fake_node = NULL; 62 63 static const node_t * 64 mock_build_state_get_exit_node(cpath_build_state_t *state) 65 { 66 (void) state; 67 68 if (!fake_node) { 69 curve25519_secret_key_t seckey; 70 curve25519_secret_key_generate(&seckey, 0); 71 72 fake_node = tor_malloc_zero(sizeof(node_t)); 73 fake_node->ri = tor_malloc_zero(sizeof(routerinfo_t)); 74 fake_node->ri->onion_curve25519_pkey = 75 tor_malloc_zero(sizeof(curve25519_public_key_t)); 76 curve25519_public_key_generate(fake_node->ri->onion_curve25519_pkey, 77 &seckey); 78 } 79 80 return fake_node; 81 } 82 83 static smartlist_t * 84 mock_node_get_link_specifier_smartlist(const node_t *node, bool direct_conn) 85 { 86 (void) node; 87 (void) direct_conn; 88 89 smartlist_t *lspecs = smartlist_new(); 90 link_specifier_t *ls_legacy = link_specifier_new(); 91 smartlist_add(lspecs, ls_legacy); 92 93 return lspecs; 94 } 95 96 static size_t relay_payload_len; 97 static uint8_t relay_payload[RELAY_PAYLOAD_SIZE]; 98 99 static int 100 mock_relay_send_command_from_edge(streamid_t stream_id, circuit_t *circ, 101 uint8_t relay_command, const char *payload, 102 size_t payload_len, 103 crypt_path_t *cpath_layer, 104 const char *filename, int lineno) 105 { 106 (void) stream_id; 107 (void) circ; 108 (void) relay_command; 109 (void) payload; 110 (void) payload_len; 111 (void) cpath_layer; 112 (void) filename; 113 (void) lineno; 114 115 memcpy(relay_payload, payload, payload_len); 116 relay_payload_len = payload_len; 117 118 return 0; 119 } 120 121 typedef struct testing_hs_pow_service_t { 122 hs_service_t service; 123 hs_subcredential_t subcred; 124 hs_service_intro_point_t *service_ip; 125 hs_desc_intro_point_t *desc_ip; 126 hs_ntor_intro_cell_keys_t intro_keys; 127 origin_circuit_t *intro_circ; 128 origin_circuit_t *rend_circ; 129 } testing_hs_pow_service_t; 130 131 /* Common test setup */ 132 static testing_hs_pow_service_t * 133 testing_hs_pow_service_new(void) 134 { 135 MOCK(build_state_get_exit_node, mock_build_state_get_exit_node); 136 MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge); 137 MOCK(launch_rendezvous_point_circuit, mock_launch_rendezvous_point_circuit); 138 MOCK(node_get_link_specifier_smartlist, 139 mock_node_get_link_specifier_smartlist); 140 141 testing_hs_pow_service_t *tsvc = tor_malloc_zero(sizeof *tsvc); 142 hs_metrics_service_init(&tsvc->service); 143 144 ed25519_keypair_t identity_keypair; 145 ed25519_keypair_generate(&identity_keypair, 0); 146 hs_helper_get_subcred_from_identity_keypair(&identity_keypair, 147 &tsvc->subcred); 148 149 curve25519_secret_key_t seckey; 150 curve25519_public_key_t pkey; 151 curve25519_secret_key_generate(&seckey, 0); 152 curve25519_public_key_generate(&pkey, &seckey); 153 154 node_t intro_node; 155 memset(&intro_node, 0, sizeof(intro_node)); 156 routerinfo_t ri; 157 memset(&ri, 0, sizeof(routerinfo_t)); 158 ri.onion_curve25519_pkey = &pkey; 159 intro_node.ri = &ri; 160 161 hs_service_intro_point_t *svc_ip = service_intro_point_new(&intro_node); 162 const ed25519_public_key_t *ip_auth_pubkey = &svc_ip->auth_key_kp.pubkey; 163 const curve25519_public_key_t *ip_enc_pubkey = &svc_ip->enc_key_kp.pubkey; 164 tsvc->service_ip = svc_ip; 165 166 ed25519_keypair_t signing_kp; 167 ed25519_keypair_generate(&signing_kp, 0); 168 tsvc->desc_ip = hs_helper_build_intro_point(&signing_kp, 0, "1.2.3.4", 0, 169 &svc_ip->auth_key_kp, 170 &svc_ip->enc_key_kp); 171 172 tsvc->intro_circ = origin_circuit_new(); 173 tsvc->rend_circ = origin_circuit_new(); 174 175 tsvc->intro_circ->cpath = tor_malloc_zero(sizeof(crypt_path_t)); 176 177 struct hs_ident_circuit_t *hs_ident = tor_malloc_zero(sizeof *hs_ident); 178 tsvc->rend_circ->hs_ident = hs_ident; 179 tsvc->intro_circ->hs_ident = hs_ident; 180 curve25519_keypair_generate(&hs_ident->rendezvous_client_kp, 0); 181 tt_int_op(0, OP_EQ, 182 hs_ntor_client_get_introduce1_keys(ip_auth_pubkey, 183 ip_enc_pubkey, 184 &hs_ident->rendezvous_client_kp, 185 &tsvc->subcred, 186 &tsvc->intro_keys)); 187 done: 188 return tsvc; 189 } 190 191 static void 192 testing_hs_pow_service_free(testing_hs_pow_service_t *tsvc) 193 { 194 hs_metrics_service_free(&tsvc->service); 195 service_intro_point_free(tsvc->service_ip); 196 hs_desc_intro_point_free(tsvc->desc_ip); 197 hs_pow_free_service_state(tsvc->service.state.pow_state); 198 tor_free(tsvc); 199 200 if (fake_node) { 201 tor_free(fake_node->ri->onion_curve25519_pkey); 202 tor_free(fake_node->ri); 203 tor_free(fake_node); 204 } 205 206 UNMOCK(build_state_get_exit_node); 207 UNMOCK(relay_send_command_from_edge_); 208 UNMOCK(launch_rendezvous_point_circuit); 209 UNMOCK(node_get_link_specifier_smartlist); 210 } 211 212 /* Make sure we can send a PoW extension to a service without PoW enabled */ 213 static void 214 test_hs_pow_unsolicited(void *arg) 215 { 216 (void)arg; 217 218 testing_hs_pow_service_t *tsvc = testing_hs_pow_service_new(); 219 220 /* Try this twice, changing only the presence or lack of PoW solution */ 221 for (int test_variant = 0; test_variant < 2; test_variant++) { 222 223 relay_payload_len = 0; 224 test_rend_launch_count = 0; 225 test_rend_launch_expect_effort = 0; 226 memset(relay_payload, 0, sizeof relay_payload); 227 228 hs_pow_solution_t solution = { 0 }; 229 int retval; 230 231 retval = hs_circ_send_introduce1(tsvc->intro_circ, tsvc->rend_circ, 232 tsvc->desc_ip, &tsvc->subcred, 233 test_variant == 0 ? &solution : NULL); 234 235 tt_int_op(retval, OP_EQ, 0); 236 tt_assert(!fast_mem_is_zero((const char*)relay_payload, 237 sizeof relay_payload)); 238 tt_int_op(relay_payload_len, OP_NE, 0); 239 240 retval = hs_circ_handle_introduce2(&tsvc->service, tsvc->intro_circ, 241 tsvc->service_ip, &tsvc->subcred, 242 relay_payload, 243 relay_payload_len); 244 245 tt_int_op(retval, OP_EQ, test_variant == 0 ? -1 : 0); 246 tt_int_op(test_rend_launch_count, OP_EQ, test_variant == 0 ? 0 : 1); 247 } 248 249 done: 250 testing_hs_pow_service_free(tsvc); 251 } 252 253 static void 254 test_hs_pow_vectors(void *arg) 255 { 256 (void)arg; 257 258 /* This covers encoding, wire protocol, and verification for PoW-extended 259 * introduction cells. The solutions here can be generated using the 260 * setup in test_hs_pow_slow. 261 */ 262 static const struct { 263 uint32_t claimed_effort; 264 uint32_t validated_effort; 265 int expected_retval; 266 const char *seed_hex; 267 const char *service_blinded_id_hex; 268 const char *nonce_hex; 269 const char *sol_hex; 270 const char *encoded_hex; 271 } vectors[] = { 272 { 273 /* All zero, expect invalid */ 274 1, 0, -1, 275 "0000000000000000000000000000000000000000000000000000000000000000", 276 "1111111111111111111111111111111111111111111111111111111111111111", 277 "00000000000000000000000000000000", "00000000000000000000000000000000", 278 "01" 279 "00000000000000000000000000000000" 280 "00000001" "00000000" 281 "00000000000000000000000000000000" 282 }, 283 { 284 /* Valid zero-effort solution */ 285 0, 0, 0, 286 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 287 "1111111111111111111111111111111111111111111111111111111111111111", 288 "55555555555555555555555555555555", "4312f87ceab844c78e1c793a913812d7", 289 "01" 290 "55555555555555555555555555555555" 291 "00000000" "aaaaaaaa" 292 "4312f87ceab844c78e1c793a913812d7" 293 }, 294 { 295 /* Valid high-effort solution */ 296 1000000, 1000000, 0, 297 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 298 "1111111111111111111111111111111111111111111111111111111111111111", 299 "59217255555555555555555555555555", "0f3db97b9cac20c1771680a1a34848d3", 300 "01" 301 "59217255555555555555555555555555" 302 "000f4240" "aaaaaaaa" 303 "0f3db97b9cac20c1771680a1a34848d3" 304 }, 305 { 306 /* Reject replays */ 307 1000000, 0, -1, 308 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 309 "1111111111111111111111111111111111111111111111111111111111111111", 310 "59217255555555555555555555555555", "0f3db97b9cac20c1771680a1a34848d3", 311 "01" 312 "59217255555555555555555555555555" 313 "000f4240" "aaaaaaaa" 314 "0f3db97b9cac20c1771680a1a34848d3" 315 }, 316 { 317 /* The claimed effort must exactly match what's in the challenge */ 318 99999, 0, -1, 319 "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f", 320 "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed", 321 "2eff9fdbc34326d9d2f18ed277469c63", "400cb091139f86b352119f6e131802d6", 322 "01" 323 "2eff9fdbc34326d9d2f18ed277469c63" 324 "0001869f" "86fb0acf" 325 "400cb091139f86b352119f6e131802d6" 326 }, 327 { 328 /* Otherwise good solution but with a corrupted nonce */ 329 100000, 0, -1, 330 "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f", 331 "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed", 332 "2eff9fdbc34326d9a2f18ed277469c63", "400cb091139f86b352119f6e131802d6", 333 "01" 334 "2eff9fdbc34326d9a2f18ed277469c63" 335 "000186a0" "86fb0acf" 336 "400cb091139f86b352119f6e131802d6" 337 }, 338 { 339 /* Corrected version of above */ 340 100000, 100000, 0, 341 "86fb0acf4932cda44dbb451282f415479462dd10cb97ff5e7e8e2a53c3767a7f", 342 "bfd298428562e530c52bdb36d81a0e293ef4a0e94d787f0f8c0c611f4f9e78ed", 343 "2eff9fdbc34326d9d2f18ed277469c63", "400cb091139f86b352119f6e131802d6", 344 "01" 345 "2eff9fdbc34326d9d2f18ed277469c63" 346 "000186a0" "86fb0acf" 347 "400cb091139f86b352119f6e131802d6" 348 } 349 }; 350 351 testing_hs_pow_service_t *tsvc = testing_hs_pow_service_new(); 352 hs_pow_service_state_t *pow_state = tor_malloc_zero(sizeof *pow_state); 353 tsvc->service.state.pow_state = pow_state; 354 tsvc->service.desc_current = service_descriptor_new(); 355 pow_state->rend_request_pqueue = smartlist_new(); 356 357 char *mem_op_hex_tmp = NULL; 358 uint8_t *decrypted = NULL; 359 trn_cell_introduce_encrypted_t *enc_cell = NULL; 360 trn_cell_introduce1_t *cell = NULL; 361 362 const unsigned num_vectors = sizeof vectors / sizeof vectors[0]; 363 for (unsigned vec_i = 0; vec_i < num_vectors; vec_i++) { 364 const int expected_retval = vectors[vec_i].expected_retval; 365 const char *service_blinded_id_hex = vectors[vec_i].service_blinded_id_hex; 366 const char *seed_hex = vectors[vec_i].seed_hex; 367 const char *nonce_hex = vectors[vec_i].nonce_hex; 368 const char *sol_hex = vectors[vec_i].sol_hex; 369 const char *encoded_hex = vectors[vec_i].encoded_hex; 370 371 relay_payload_len = 0; 372 test_rend_launch_count = 0; 373 test_rend_launch_expect_effort = vectors[vec_i].validated_effort; 374 memset(relay_payload, 0, sizeof relay_payload); 375 376 hs_pow_solution_t solution = { 377 .effort = vectors[vec_i].claimed_effort, 378 }; 379 int retval; 380 381 tt_int_op(strlen(service_blinded_id_hex), OP_EQ, 2 * HS_POW_ID_LEN); 382 tt_int_op(strlen(seed_hex), OP_EQ, 2 * HS_POW_SEED_LEN); 383 tt_int_op(strlen(nonce_hex), OP_EQ, 2 * sizeof solution.nonce); 384 tt_int_op(strlen(sol_hex), OP_EQ, 2 * sizeof solution.equix_solution); 385 386 tt_assert(tsvc->service.desc_current); 387 ed25519_public_key_t *desc_blinded_pubkey = 388 &tsvc->service.desc_current->desc->plaintext_data.blinded_pubkey; 389 390 tt_int_op(base16_decode((char*)desc_blinded_pubkey->pubkey, 391 HS_POW_ID_LEN, service_blinded_id_hex, 392 2 * HS_POW_ID_LEN), 393 OP_EQ, HS_POW_ID_LEN); 394 tt_int_op(base16_decode((char*)pow_state->seed_previous, HS_POW_SEED_LEN, 395 seed_hex, 2 * HS_POW_SEED_LEN), 396 OP_EQ, HS_POW_SEED_LEN); 397 tt_int_op(base16_decode((char*)solution.nonce, HS_POW_NONCE_LEN, 398 nonce_hex, 2 * HS_POW_NONCE_LEN), 399 OP_EQ, HS_POW_NONCE_LEN); 400 tt_int_op(base16_decode((char*)solution.equix_solution, HS_POW_EQX_SOL_LEN, 401 sol_hex, 2 * HS_POW_EQX_SOL_LEN), 402 OP_EQ, HS_POW_EQX_SOL_LEN); 403 404 ed25519_pubkey_copy(&tsvc->service_ip->blinded_id, desc_blinded_pubkey); 405 memcpy(solution.seed_head, pow_state->seed_previous, HS_POW_SEED_HEAD_LEN); 406 407 /* Try to encode 'solution' into a relay cell */ 408 409 retval = hs_circ_send_introduce1(tsvc->intro_circ, tsvc->rend_circ, 410 tsvc->desc_ip, &tsvc->subcred, 411 &solution); 412 413 tt_int_op(retval, OP_EQ, 0); 414 tt_assert(!fast_mem_is_zero((const char*)relay_payload, 415 sizeof relay_payload)); 416 tt_int_op(relay_payload_len, OP_NE, 0); 417 418 /* Check the service's response to this introduction */ 419 420 retval = hs_circ_handle_introduce2(&tsvc->service, tsvc->intro_circ, 421 tsvc->service_ip, &tsvc->subcred, 422 relay_payload, 423 relay_payload_len); 424 tt_int_op(retval, OP_EQ, expected_retval); 425 tt_int_op(test_rend_launch_count, OP_EQ, expected_retval == 0 ? 1 : 0); 426 427 /* Start unpacking the cell ourselves so we can check the PoW data */ 428 429 trn_cell_introduce1_free(cell); 430 cell = NULL; 431 tt_int_op(trn_cell_introduce1_parse(&cell, relay_payload, 432 relay_payload_len), OP_GT, 0); 433 434 size_t encrypted_section_len; 435 const uint8_t *encrypted_section; 436 encrypted_section = trn_cell_introduce1_getconstarray_encrypted(cell); 437 encrypted_section_len = trn_cell_introduce1_getlen_encrypted(cell); 438 tt_int_op(encrypted_section_len, OP_GT, 439 DIGEST256_LEN + CURVE25519_PUBKEY_LEN); 440 441 /* Decrypt the encrypted portion of the INTRODUCE1 */ 442 443 crypto_cipher_t *cipher = NULL; 444 cipher = crypto_cipher_new_with_bits((char *) tsvc->intro_keys.enc_key, 445 CURVE25519_PUBKEY_LEN * 8); 446 tt_ptr_op(cipher, OP_NE, NULL); 447 448 size_t decrypted_len = encrypted_section_len 449 - DIGEST256_LEN - CURVE25519_PUBKEY_LEN; 450 tor_free(decrypted); 451 decrypted = tor_malloc_zero(decrypted_len); 452 retval = crypto_cipher_decrypt(cipher, (char *) decrypted, 453 (const char *) encrypted_section 454 + CURVE25519_PUBKEY_LEN, 455 decrypted_len); 456 crypto_cipher_free(cipher); 457 tt_int_op(retval, OP_EQ, 0); 458 459 /* Parse the outer layer of the encrypted payload */ 460 461 trn_cell_introduce_encrypted_free(enc_cell); 462 enc_cell = NULL; 463 tt_int_op(trn_cell_introduce_encrypted_parse(&enc_cell, decrypted, 464 decrypted_len), OP_GT, 0); 465 466 /* Check for the expected single extension */ 467 468 const trn_extension_t *extensions = 469 trn_cell_introduce_encrypted_get_extensions(enc_cell); 470 tt_int_op(trn_extension_get_num(extensions), OP_EQ, 1); 471 472 const trn_extension_field_t *field = 473 trn_extension_getconst_fields(extensions, 0); 474 tt_int_op(trn_extension_field_get_field_type(field), 475 OP_EQ, TRUNNEL_EXT_TYPE_POW); 476 477 const uint8_t *field_data = trn_extension_field_getconstarray_field(field); 478 size_t field_len = trn_extension_field_getlen_field(field); 479 480 /* Our test vectors cover the packed data in the single extension */ 481 482 tt_int_op(field_len * 2, OP_EQ, strlen(encoded_hex)); 483 test_memeq_hex(field_data, encoded_hex); 484 } 485 486 done: 487 tor_free(mem_op_hex_tmp); 488 tor_free(decrypted); 489 trn_cell_introduce1_free(cell); 490 trn_cell_introduce_encrypted_free(enc_cell); 491 service_descriptor_free(tsvc->service.desc_current); 492 testing_hs_pow_service_free(tsvc); 493 hs_pow_remove_seed_from_cache(NULL); 494 } 495 496 struct testcase_t hs_pow_tests[] = { 497 { "unsolicited", test_hs_pow_unsolicited, TT_FORK, NULL, NULL }, 498 { "vectors", test_hs_pow_vectors, TT_FORK, NULL, NULL }, 499 END_OF_TESTCASES 500 };