On Wed, 6 Sep 2023, procmem@xxxxxxxxxx wrote: > Hi, Whonix OS privacy dev here. I had a discussion concerning the new > ObscureKeystrokeTiming feature with a prominent researcher and author of the > mouse and keyboard biometrics obfuscation tool called Kloak. While it's > exciting to see keystroke obfuscation measures [1] start to become more > prevalent mainstream, the current implementation of using a 50Hz fixed packet > timing has the potential to create fingerprinting risks for hosts. Reason > being, not all computer clocks have the exact same precision. Some may > oscillate slightly faster or slower because of the physical discrepancies of > clock crystals. A network adversary monitoring connections on the clearnet > could potentially link future ones of the same host even if routed through an > anonymity network like Tor. > > Advanced attacks where attackers run loads on onion services that influence > CPU activity and clock skew in predictable ways [2] may be possibly used to > deanonymize them. > > We would suggest drawing the padding packet intervals from some other > distribution instead of firing these off on a fixed timer. Basically, do what > kloak does but at the network layer. Yeah, making the intervals a bit uncertain seems like a reasonable idea. This gives them 10% jitter. diff --git a/clientloop.c b/clientloop.c index b461917..73cdd09 100644 --- a/clientloop.c +++ b/clientloop.c @@ -109,6 +109,9 @@ /* Permitted RSA signature algorithms for UpdateHostkeys proofs */ #define HOSTKEY_PROOF_RSA_ALGS "rsa-sha2-512,rsa-sha2-256" +/* Uncertainty (in percent) of keystroke timing intervals */ +#define SSH_KEYSTROKE_TIMING_FUZZ 10 + /* import options */ extern Options options; @@ -519,6 +522,33 @@ send_chaff(struct ssh *ssh) return 1; } +/* Sets the next interval to send a keystroke or chaff packet */ +static void +set_next_interval(const struct timespec *now, struct timespec *next_interval, + u_int interval_ms, u_int interval_fuzz_pct) +{ + struct timespec tmp; + long long interval_ns, fuzz_ns; + + interval_ns = interval_ms * (1000LL * 1000); + fuzz_ns = (interval_ns * interval_fuzz_pct) / 100; + /* Center fuzz around requested interval */ + if (fuzz_ns > INT_MAX) + fuzz_ns = INT_MAX; + if (fuzz_ns > interval_ns) { + /* Shouldn't happen */ + fatal_f("internal error: fuzz %u%% %lldns > interval %lldns", + interval_fuzz_pct, fuzz_ns, interval_ns); + } + interval_ns -= fuzz_ns / 2; + interval_ns += arc4random_uniform(fuzz_ns); + + tmp.tv_sec = interval_ns / (1000 * 1000 * 1000); + tmp.tv_nsec = interval_ns % (1000 * 1000 * 1000); + + timespecadd(now, &tmp, next_interval); +} + /* * Performs keystroke timing obfuscation. Returns non-zero if the * output fd should be polled. @@ -586,8 +616,9 @@ obfuscate_keystroke_timing(struct ssh *ssh, struct timespec *timeout, options.obscure_keystroke_timing_interval); just_started = had_keystroke = active = 1; nchaff = 0; - ms_to_timespec(&tmp, options.obscure_keystroke_timing_interval); - timespecadd(&now, &tmp, &next_interval); + set_next_interval(&now, &next_interval, + options.obscure_keystroke_timing_interval, + SSH_KEYSTROKE_TIMING_FUZZ); } /* Don't hold off if obfuscation inactive */ @@ -620,8 +651,9 @@ obfuscate_keystroke_timing(struct ssh *ssh, struct timespec *timeout, n = (n < 0) ? 1 : n + 1; /* Advance to the next interval */ - ms_to_timespec(&tmp, options.obscure_keystroke_timing_interval * n); - timespecadd(&now, &tmp, &next_interval); + set_next_interval(&now, &next_interval, + options.obscure_keystroke_timing_interval * n, + SSH_KEYSTROKE_TIMING_FUZZ); return 1; } _______________________________________________ openssh-unix-dev mailing list openssh-unix-dev@xxxxxxxxxxx https://lists.mindrot.org/mailman/listinfo/openssh-unix-dev