commit 6f6881c4324f35d44b997591939de7e847cca7a3 Author: David Goulet dgoulet@ev0ke.net Date: Mon Apr 20 15:51:06 2015 -0400
Use a random count of INTRODUCE2 for IP rotation
An introduction point is currently rotated when the amount of INTRODUCE2 cells reached a fixed value of 16384. This makes it pretty easy for an attacker to inflate that number and observe when the IP rotates which leaks the popularity of the HS (amount of client that passed through the IP).
This commit makes it a random count between the current value of 16384 and two times that.
Fixes #15745
Signed-off-by: David Goulet dgoulet@ev0ke.net --- changes/bug15745 | 7 +++++++ src/or/or.h | 19 +++++++++++++------ src/or/rendservice.c | 17 +++++++++++------ 3 files changed, 31 insertions(+), 12 deletions(-)
diff --git a/changes/bug15745 b/changes/bug15745 new file mode 100644 index 0000000..6e4bfa4 --- /dev/null +++ b/changes/bug15745 @@ -0,0 +1,7 @@ + o Minor feature (HS popularity countermeasure): + - To avoid leaking HS popularity, don't cycle the introduction point + when we've handled a fixed number of INTRODUCE2 cells but instead + cycle it when a random value of introductions is reached thus making + it more difficult for an attacker to find out the amount of clients + that has passed through the introduction point for a specific HS. + Closes ticket 15745. diff --git a/src/or/or.h b/src/or/or.h index d548aea..4fd6d1d 100644 --- a/src/or/or.h +++ b/src/or/or.h @@ -4805,12 +4805,13 @@ typedef struct rend_encoded_v2_service_descriptor_t { * introduction point. See also rend_intro_point_t.unreachable_count. */ #define MAX_INTRO_POINT_REACHABILITY_FAILURES 5
-/** The maximum number of distinct INTRODUCE2 cells which a hidden - * service's introduction point will receive before it begins to - * expire. - * - * XXX023 Is this number at all sane? */ -#define INTRO_POINT_LIFETIME_INTRODUCTIONS 16384 +/** The minimum and maximum number of distinct INTRODUCE2 cells which a + * hidden service's introduction point will receive before it begins to + * expire. */ +#define INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS 16384 +/* Double the minimum value so the interval is [min, min * 2]. */ +#define INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS \ + (INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS * 2)
/** The minimum number of seconds that an introduction point will last * before expiring due to old age. (If it receives @@ -4864,6 +4865,12 @@ typedef struct rend_intro_point_t { */ int accepted_introduce2_count;
+ /** (Service side only) Number of maximum INTRODUCE2 cells that this IP + * will accept. This is a random value between + * INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS and + * INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS. */ + unsigned int max_introductions; + /** (Service side only) The time at which this intro point was first * published, or -1 if this intro point has not yet been * published. */ diff --git a/src/or/rendservice.c b/src/or/rendservice.c index c1c0c46..cf0352c 100644 --- a/src/or/rendservice.c +++ b/src/or/rendservice.c @@ -1158,16 +1158,17 @@ rend_service_note_removing_intro_point(rend_service_t *service, /* This intro point was never used. Don't change * n_intro_points_wanted. */ } else { + /* We want to increase the number of introduction points service * operates if intro was heavily used, or decrease the number of * intro points if intro was lightly used. * * We consider an intro point's target 'usage' to be - * INTRO_POINT_LIFETIME_INTRODUCTIONS introductions in + * maximum of INTRODUCE2 cells divided by * INTRO_POINT_LIFETIME_MIN_SECONDS seconds. To calculate intro's - * fraction of target usage, we divide the fraction of - * _LIFETIME_INTRODUCTIONS introductions that it has handled by - * the fraction of _LIFETIME_MIN_SECONDS for which it existed. + * fraction of target usage, we divide the amount of INTRODUCE2 cells + * that it has handled by the fraction of _LIFETIME_MIN_SECONDS for + * which it existed. * * Then we multiply that fraction of desired usage by a fudge * factor of 1.5, to decide how many new introduction points @@ -1189,7 +1190,7 @@ rend_service_note_removing_intro_point(rend_service_t *service, intro_point_accepted_intro_count(intro) / (double)(now - intro->time_published); const double intro_point_target_usage = - INTRO_POINT_LIFETIME_INTRODUCTIONS / + intro->max_introductions / (double)INTRO_POINT_LIFETIME_MIN_SECONDS; const double fractional_n_intro_points_wanted_to_replace_this_one = (1.5 * (intro_point_usage / intro_point_target_usage)); @@ -3123,7 +3124,7 @@ intro_point_should_expire_now(rend_intro_point_t *intro, }
if (intro_point_accepted_intro_count(intro) >= - INTRO_POINT_LIFETIME_INTRODUCTIONS) { + intro->max_introductions) { /* This intro point has been used too many times. Expire it now. */ return 1; } @@ -3335,6 +3336,10 @@ rend_services_introduce(void) intro->time_published = -1; intro->time_to_expire = -1; intro->time_expiring = -1; + intro->max_introductions = + INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS + + crypto_rand_int(INTRO_POINT_MAX_LIFETIME_INTRODUCTIONS - + INTRO_POINT_MIN_LIFETIME_INTRODUCTIONS); smartlist_add(service->intro_nodes, intro); log_info(LD_REND, "Picked router %s as an intro point for %s.", safe_str_client(node_describe(node)),