From 26957b47ac8aff33e8c6a6de2a227c0182749206 Mon Sep 17 00:00:00 2001 From: David Goulet Date: Tue, 28 Jun 2022 11:09:43 -0400 Subject: [PATCH] hs: Descriptor support for PoW Signed-off-by: David Goulet --- src/feature/dirparse/parsecommon.h | 1 + src/feature/hs/hs_descriptor.c | 115 +++++++++++++++++++++++++++++++++++++ src/feature/hs/hs_descriptor.h | 3 + src/feature/hs/hs_pow.h | 9 ++- 4 files changed, 126 insertions(+), 2 deletions(-) diff --git a/src/feature/dirparse/parsecommon.h b/src/feature/dirparse/parsecommon.h index 675c5f68d5..9333ec4b27 100644 --- a/src/feature/dirparse/parsecommon.h +++ b/src/feature/dirparse/parsecommon.h @@ -173,6 +173,7 @@ typedef enum { R3_DESC_AUTH_CLIENT, R3_ENCRYPTED, R3_FLOW_CONTROL, + R3_POW_PARAMS, R_IPO_IDENTIFIER, R_IPO_IP_ADDRESS, diff --git a/src/feature/hs/hs_descriptor.c b/src/feature/hs/hs_descriptor.c index 15ad9d8efb..27152a8bf0 100644 --- a/src/feature/hs/hs_descriptor.c +++ b/src/feature/hs/hs_descriptor.c @@ -68,6 +68,7 @@ #include "feature/dirparse/parsecommon.h" #include "feature/hs/hs_cache.h" #include "feature/hs/hs_config.h" +#include "feature/hs/hs_pow.h" #include "feature/nodelist/torcert.h" /* tor_cert_encode_ed22519() */ #include "lib/memarea/memarea.h" #include "lib/crypt_ops/crypto_format.h" @@ -96,6 +97,7 @@ #define str_ip_legacy_key_cert "legacy-key-cert" #define str_intro_point_start "\n" str_intro_point " " #define str_flow_control "flow-control" +#define str_pow_params "pow-params" /* Constant string value for the construction to encrypt the encrypted data * section. */ #define str_enc_const_superencryption "hsdir-superencrypted-data" @@ -117,6 +119,16 @@ static const struct { { 0, NULL } }; +/** PoW supported types. */ +static const struct { + hs_pow_desc_type_t type; + const char *identifier; +} pow_types[] = { + { HS_POW_DESC_V1, "v1"}, + /* Indicate end of array. */ + { 0, NULL } +}; + /** Descriptor ruleset. */ static token_rule_t hs_desc_v3_token_table[] = { T1_START(str_hs_desc, R_HS_DESCRIPTOR, EQ(1), NO_OBJ), @@ -143,6 +155,7 @@ static token_rule_t hs_desc_encrypted_v3_token_table[] = { T01(str_intro_auth_required, R3_INTRO_AUTH_REQUIRED, GE(1), NO_OBJ), T01(str_single_onion, R3_SINGLE_ONION_SERVICE, ARGS, NO_OBJ), T01(str_flow_control, R3_FLOW_CONTROL, GE(2), NO_OBJ), + T01(str_pow_params, R3_POW_PARAMS, GE(3), NO_OBJ), END_OF_TABLE }; @@ -777,6 +790,31 @@ get_inner_encrypted_layer_plaintext(const hs_descriptor_t *desc) protover_get_supported(PRT_FLOWCTRL), congestion_control_sendme_inc()); } + + /* Add PoW parameters if present. */ + if (desc->encrypted_data.pow_params) { + /* Base64 the seed */ + size_t seed_b64_len = base64_encode_size(HS_POW_SEED_LEN, 0) + 1; + char *seed_b64 = tor_malloc_zero(seed_b64_len); + int ret = base64_encode(seed_b64, seed_b64_len, + (char *)desc->encrypted_data.pow_params->seed, + HS_POW_SEED_LEN, 0); + /* Return length doesn't count the NUL byte. */ + tor_assert((size_t) ret == (seed_b64_len - 1)); + + /* Convert the expiration time to space-less ISO format. */ + char time_buf[ISO_TIME_LEN + 1]; + format_iso_time_nospace(time_buf, + desc->encrypted_data.pow_params->expiration_time); + + /* Add "pow-params" line to descriptor encoding. */ + smartlist_add_asprintf(lines, "%s %s %s %u %s\n", str_pow_params, + pow_types[desc->encrypted_data.pow_params->type].identifier, + seed_b64, + desc->encrypted_data.pow_params->suggested_effort, + time_buf); + tor_free(seed_b64); + } } /* Build the introduction point(s) section. */ @@ -2053,6 +2091,70 @@ desc_sig_is_valid(const char *b64_sig, return ret; } +/** Given the token tok for PoW params, decode it as hs_pow_desc_params_t. + * tok->args MUST contain at least 4 elements Return 0 on success else -1 on + * failure. */ +static int +decode_pow_params(const directory_token_t *tok, + hs_pow_desc_params_t *pow_params) +{ + int ret = -1; + + tor_assert(tok); + tor_assert(tok->n_args >= 4); + tor_assert(pow_params); + + /* Find the type of PoW system being used. */ + int match = 0; + for (int idx = 0; pow_types[idx].identifier; idx++) { + if (!strncmp(tok->args[0], pow_types[idx].identifier, + strlen(pow_types[idx].identifier))) { + pow_params->type = pow_types[idx].type; + match = 1; + break; + } + } + if (!match) { + log_warn(LD_REND, "Unknown PoW type from descriptor."); + goto done; + } + + if (base64_decode((char *)pow_params->seed, sizeof(pow_params->seed), + tok->args[1], strlen(tok->args[1])) != + sizeof(pow_params->seed)) { + log_warn(LD_REND, "Unparseable seed %s in PoW params", + escaped(tok->args[1])); + goto done; + } + + int ok; + unsigned long effort = + tor_parse_ulong(tok->args[2], 10, 1, UINT32_MAX, &ok, NULL); + if (!ok) { + log_warn(LD_REND, "Unparseable suggested effort %s in PoW params", + escaped(tok->args[2])); + goto done; + } + pow_params->suggested_effort = effort; + + /* Parse the expiration time of the PoW params. */ + time_t expiration_time = 0; + if (parse_iso_time_nospace(tok->args[3], &expiration_time)) { + log_warn(LD_REND, "Unparseable expiration time %s in PoW params", + escaped(tok->args[3])); + goto done; + } + /* Validation of this time is done in client_desc_has_arrived() so we can + * trigger a fetch if expired. */ + pow_params->expiration_time = expiration_time; + + /* Success. */ + ret = 0; + + done: + return ret; +} + /** Decode descriptor plaintext data for version 3. Given a list of tokens, an * allocated plaintext object that will be populated and the encoded * descriptor with its length. The last one is needed for signature @@ -2364,6 +2466,18 @@ desc_decode_encrypted_v3(const hs_descriptor_t *desc, desc_encrypted_out->sendme_inc = sendme_inc; } + /* Get PoW if any. */ + tok = find_opt_by_keyword(tokens, R3_POW_PARAMS); + if (tok) { + hs_pow_desc_params_t *pow_params = + tor_malloc_zero(sizeof(hs_pow_desc_params_t)); + if (decode_pow_params(tok, pow_params)) { + tor_free(pow_params); + goto err; + } + desc_encrypted_out->pow_params = pow_params; + } + /* Initialize the descriptor's introduction point list before we start * decoding. Having 0 intro point is valid. Then decode them all. */ desc_encrypted_out->intro_points = smartlist_new(); @@ -2775,6 +2889,7 @@ hs_desc_encrypted_data_free_contents(hs_desc_encrypted_data_t *desc) smartlist_free(desc->intro_points); } tor_free(desc->flow_control_pv); + tor_free(desc->pow_params); memwipe(desc, 0, sizeof(*desc)); } diff --git a/src/feature/hs/hs_descriptor.h b/src/feature/hs/hs_descriptor.h index fbfc4715a9..c89dc0b580 100644 --- a/src/feature/hs/hs_descriptor.h +++ b/src/feature/hs/hs_descriptor.h @@ -172,6 +172,9 @@ typedef struct hs_desc_encrypted_data_t { char *flow_control_pv; uint8_t sendme_inc; + /** PoW parameters. If NULL, it is not present. */ + hs_pow_desc_params_t *pow_params; + /** A list of intro points. Contains hs_desc_intro_point_t objects. */ smartlist_t *intro_points; } hs_desc_encrypted_data_t; diff --git a/src/feature/hs/hs_pow.h b/src/feature/hs/hs_pow.h index cd4f228565..7f5e297470 100644 --- a/src/feature/hs/hs_pow.h +++ b/src/feature/hs/hs_pow.h @@ -37,10 +37,15 @@ typedef unsigned __int128 uint128_t; #define HS_POW_CHALLENGE_LEN \ (HS_POW_SEED_LEN + HS_POW_NONCE_LEN + HS_POW_EFFORT_LEN) +/** Type of PoW in the descriptor. */ +typedef enum { + HS_POW_DESC_V1 = 1, +} hs_pow_desc_type_t; + /** Proof-of-Work parameters for DoS defense located in a descriptor. */ typedef struct hs_pow_desc_params_t { - /** Type of PoW system being used, for example "v1". */ - char *type; + /** Type of PoW system being used. */ + hs_pow_desc_type_t type; /** Random 32-byte seed used as input the the PoW hash function. Decoded? */ uint8_t seed[HS_POW_SEED_LEN]; -- 2.11.4.GIT