keystroke timing attack

C. Jason Coit jasonc at silicondefense.com
Wed Nov 28 11:48:51 EST 2001


Solar,

Thanks for the tips.  I am new at this patch submission thing.  The
theory of our implementation is sound, the client and server should both
send at regular intervals (and as you said the protocol could be
extended to negotiate this).  The regular intervals will completely
erase the inter keystroke timing analysis ability.  As with our
implementation the server should be allowed to send bursty responses
back when responding with copious amounts of data.  The reason the
parameters were hard coded was to avoid the negotiation for now.  

This type of countermeasure should be on by default so people actually
use it.  I implemented it could be turned off via command line but I
agree that should be something in the start up config. So I went ahead 
and coded this up.  

One reason for my late response is I had just finished one fix for
openssh3.0p1 when openssh3.0.1p1 was released.  I ported the code again
and checked for compatibility with ssh1 and ssh2.  I have attached a
patch for our latest implementation for openssh 3.0.1p1.  

As far as the comments go, I wanted to document all the changes I had
made to be in compliance with the license agreement and the header
comment was to explain what the code did overall.  I agree this should
be moved to credits file. The comments are the primary reason for the
file being so large.  I tried to explain my changes so the openssh
coders could see what I was doing.  Should I remove the comments
altogether so it is easier to check the code?  I stripped the verbose
comments this time.

The floating point faux pas is fixed. It's a mod operation right now.
I had originally copied the op from another function in openssh but 
it makes more sense to use modular arithmetic instead.

As far as X11 forwarding goes, I agree that it would take considerably
more bogus data to hide X11 data. It's questionable if that adds any
security, X11 data in itself is quite obtuse already. The goal of
the patch is to make the keystroke timing attack a lot harder
nut to crack, than it was before for interactive terminals not X11
forwarding.  

The final solution to this problem would involve updating the ssh
protocol to facilitate server and client negotiations on timing and
bogus packet sizes.  For our fixed timing and packet size
implementation, the overhead is not bad at all.  Since we are concerned
only with terminal traffic, our 
implementation sends a 48 bytes of data in every packet every 50 ms.  

Recall our we also conserve bandwidth since our implementation shuts
down after about 1 second after the last valid keystroke (in particular
900 ms each 50 interval has 1/5 chance of being shut off until the next
valid keystroke is pressed).

Our implementation doesn't limit your typing speed:
(1 char/50 ms )* (1000 ms/ 1 sec)*(60 sec/ 1 min)
= 1200 chars/min 
If someone types on average 7 character words,
1200 chars/min * 1 word/7 chars = 171.43 words/min (round 2 dec. digits)
So unless someone is a very fast typist, this should not effect the rate
at which they type. 

Now if the server needs to send more data than 48 bytes in response to a
query from the client, we allow it to send this normally (as fast or as
slow as it usually would). This bursty ability on the server side allows
for quick responses to commands.  Both the client and server have this
bursty nature if they are required to send data larger than the single
character packet.  It does give away that the server is responding to
some query by the client, but it does not leak the inter keystroke
timing.

So please give me more comments on our approach.  I am eager to create a
valuable patch for the openssh community.

regards,

Jason 


+--                                  --+
|   C. Jason Coit Programmer/Analyst   |
|    Silicon Defense: IDS Solutions    |
|    http://www.silicondefense.com/    |
+--                                   -+
-------------- next part --------------
--- channels.c	Thu Oct 11 18:35:05 2001
+++ channels.new.c	Mon Nov 26 13:37:04 2001
@@ -26,6 +26,10 @@
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
+ *
+ * Keystroke Timing Analysis Evasion Implementation:
+ * Copyright (c) 2001 Silicon Defense. All rights reserved. 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
@@ -1601,9 +1605,10 @@
 /* If there is data to send to the connection, enqueue some of it now. */
 
 void
-channel_output_poll()
+channel_output_poll(int *bogus_send_count, int use_steno_timing_manipulation)
 {
 	int len, i;
+	u_int32_t rand_shutoff;
 	Channel *c;
 
 	for (i = 0; i < channels_alloc; i++) {
@@ -1629,7 +1634,6 @@
 			debug3("channel %d: will not send data after close", c->self);
 			continue;
 		}
-
 		/* Get the amount of buffered data for this channel. */
 		if ((c->istate == CHAN_INPUT_OPEN ||
 		    c->istate == CHAN_INPUT_WAIT_DRAIN) &&
@@ -1658,10 +1662,39 @@
 				    SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA);
 				packet_put_int(c->remote_id);
 				packet_put_string(buffer_ptr(&c->input), len);
+
+				if(compat20 && use_steno_timing_manipulation) {
+					(*bogus_send_count)=0;
+					debug2("reseting count");
+				}
+
 				packet_send();
 				buffer_consume(&c->input, len);
 				c->remote_window -= len;
 			}
+		} else if ((len = buffer_len(&c->input)) == 0
+			&& !(c->istate == CHAN_INPUT_WAIT_DRAIN)
+			&& use_steno_timing_manipulation && compat20) {
+
+			if((*bogus_send_count) < 18){
+				debug2("sending garbage packet");
+				packet_send_ignore(16);
+				packet_send();
+				(*bogus_send_count)++;
+			} else if((*bogus_send_count) != 20) {
+
+				rand_shutoff = arc4random()%5;
+				debug("Random is:%d",rand_shutoff);
+				if(!rand_shutoff) {
+					(*bogus_send_count) = 20;
+					debug("Random shutoff: stop sending garbage packets");
+				} else {
+					debug2("sending garbage packet");
+					packet_send_ignore(16);
+					packet_send();
+				}
+			} else
+				debug("Random Shutoff past 900ms occurred: stop sending garbage packets");
 		} else if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
 			if (compat13)
 				fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3");
--- channels.h	Sun Nov 11 16:04:55 2001
+++ channels.new.h	Mon Nov 26 13:37:04 2001
@@ -21,6 +21,9 @@
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
+ * Keystroke Timing Analysis Evasion Implementation:
+ * Copyright (c) 2001 Silicon Defense. All rights reserved. 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
@@ -172,7 +175,7 @@
 
 void	 channel_prepare_select(fd_set **, fd_set **, int *, int*, int);
 void     channel_after_select(fd_set *, fd_set *);
-void     channel_output_poll(void);
+void     channel_output_poll(int *, int);
 
 int      channel_not_very_much_buffered_data(void);
 void     channel_close_all(void);
--- clientloop.c	Sun Nov 11 16:06:33 2001
+++ clientloop.new.c	Mon Nov 26 13:37:04 2001
@@ -22,6 +22,9 @@
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
+ * Keystroke Timing Analysis Evasion Implementation:
+ * Copyright (c) 2001 Silicon Defense. All rights reserved. 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
@@ -316,10 +319,14 @@
  * one of the file descriptors).
  */
 
-static void
+static int
 client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp,
     int *maxfdp, int *nallocp, int rekeying)
 {
+	static struct timeval steno_timer = {0, 50000};
+	int return_val = 0;
+	long int prev_timer_val = 0;
+
 	/* Add any selections by the channel mechanism. */
 	channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, rekeying);
 
@@ -343,15 +350,7 @@
 			FD_SET(fileno(stderr), *writesetp);
 	} else {
 		/* channel_prepare_select could have closed the last channel */
-		if (session_closed && !channel_still_open() &&
-		    !packet_have_data_to_write()) {
-			/* clear mask since we did not call select() */
-			memset(*readsetp, 0, *nallocp);
-			memset(*writesetp, 0, *nallocp);
-			return;
-		} else {
-			FD_SET(connection_in, *readsetp);
-		}
+                FD_SET(connection_in, *readsetp);
 	}
 
 	/* Select server connection if have data to write to the server. */
@@ -366,10 +365,28 @@
 	 * it: just have a random timeout for the select, and send a random
 	 * SSH_MSG_IGNORE packet when the timeout expires.
 	 */
+        
+        if(options.use_steno_timing_manipulation)
+	  {
+	
+	    prev_timer_val = steno_timer.tv_usec;
+	    
+	    return_val = select((*maxfdp)+1, *readsetp, *writesetp, NULL,
+				&steno_timer);
+	    
+	    if(prev_timer_val == steno_timer.tv_usec){
+	      debug3("decrementing timer forcefully");
+		steno_timer.tv_usec -= 100;    
+	    }
+	  }
+	else {
+	  return_val = select((*maxfdp)+1, *readsetp, *writesetp, NULL, NULL);
+	}
+	  
 
-	if (select((*maxfdp)+1, *readsetp, *writesetp, NULL, NULL) < 0) {
-		char buf[100];
+	if(return_val < 0){
 
+		char buf[100];
 		/*
 		 * We have to clear the select masks, because we return.
 		 * We have to return, because the mainloop checks for the flags
@@ -379,14 +396,27 @@
 		memset(*writesetp, 0, *nallocp);
 
 		if (errno == EINTR)
-			return;
+			return 0;
 		/* Note: we might still have data in the buffers. */
 		snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno));
 		buffer_append(&stderr_buffer, buf, strlen(buf));
 		quit_pending = 1;
 	}
+	
+	if(options.use_steno_timing_manipulation){
+	  if(steno_timer.tv_usec <= 0) {
+	    steno_timer.tv_usec = 50000;
+	    return 1;
+	  }
+	  else
+	    return 0;
+	}
+	else{
+	  return 1;
+	}
 }
 
+
 static void
 client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr)
 {
@@ -779,6 +809,9 @@
 	double start_time, total_time;
 	int max_fd = 0, max_fd2 = 0, len, rekeying = 0, nalloc = 0;
 	char buf[100];
+	int bogus_send_count = 0;
+	int time_out = 0;
+
 
 	debug("Entering interactive session.");
 
@@ -837,9 +870,11 @@
 		if (session_ident != -1)
 			channel_register_cleanup(session_ident,
 			    client_channel_closed);
+	       
 	} else {
-		/* Check if we should immediately send eof on stdin. */
-		client_check_initial_eof_on_stdin();
+	  options.use_steno_timing_manipulation = 0;
+	  /* Check if we should immediately send eof on stdin. */
+	  client_check_initial_eof_on_stdin();
 	}
 
 	/* Main loop of the client for the interactive session mode. */
@@ -860,16 +895,30 @@
 			 * Make packets of buffered stdin data, and buffer
 			 * them for sending to the server.
 			 */
-			if (!compat20)
-				client_make_packets_from_stdin_data();
-
+		        if (!compat20)
+			  client_make_packets_from_stdin_data();
+			
 			/*
 			 * Make packets from buffered channel data, and
 			 * enqueue them for sending to the server.
 			 */
-			if (packet_not_very_much_data_to_write())
-				channel_output_poll();
 
+			if(options.use_steno_timing_manipulation) {
+			  if(time_out){
+			    channel_output_poll(&bogus_send_count,
+				     options.use_steno_timing_manipulation);
+			    time_out = 0;
+			  }
+			}
+			else{
+			  if (packet_not_very_much_data_to_write())
+			    {
+			      channel_output_poll(&bogus_send_count,
+                                                options.use_steno_timing_manipulation);
+			    
+			    }
+			}
+		
 			/*
 			 * Check if the window size has changed, and buffer a
 			 * message about it to the server if so.
@@ -878,14 +927,14 @@
 
 			if (quit_pending)
 				break;
-		}
+	        }
 		/*
 		 * Wait until we have something to do (something becomes
 		 * available on one of the descriptors).
 		 */
 		max_fd2 = max_fd;
-		client_wait_until_can_do_something(&readset, &writeset,
-		    &max_fd2, &nalloc, rekeying);
+		time_out = client_wait_until_can_do_something(&readset,
+			 &writeset, &max_fd2, &nalloc, rekeying);
 
 		if (quit_pending)
 			break;
@@ -1226,6 +1275,13 @@
 	} else if (strcmp(rtype, "exit-status") == 0) {
 		success = 1;
 		exit_status = packet_get_int();
+		packet_done();
+	} else if (strcmp(rtype, "no_steno") == 0) {
+		debug("received request not use use steno");
+		if(options.use_steno_timing_manipulation) {
+			options.use_steno_timing_manipulation = 0;
+		}
+		success = 1;
 		packet_done();
 	}
 	if (reply) {
--- readconf.c	Wed Oct  3 10:39:39 2001
+++ readconf.new.c	Mon Nov 26 13:37:04 2001
@@ -115,7 +115,8 @@
 	oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias,
 	oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication,
 	oHostKeyAlgorithms, oBindAddress, oSmartcardDevice,
-	oClearAllForwardings, oNoHostAuthenticationForLocalhost 
+	oClearAllForwardings, oNoHostAuthenticationForLocalhost,
+	oUseStenoTimingManipulation
 } OpCodes;
 
 /* Textual representations of the tokens. */
@@ -186,7 +187,8 @@
 	{ "bindaddress", oBindAddress },
 	{ "smartcarddevice", oSmartcardDevice },
 	{ "clearallforwardings", oClearAllForwardings }, 
-	{ "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost }, 
+	{ "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost },
+	{ "usestenotimingmanipulation", oUseStenoTimingManipulation },
 	{ NULL, 0 }
 };
 
@@ -678,6 +680,24 @@
 			*intptr = value;
 		break;
 
+	case oUseStenoTimingManipulation:
+		intptr = &options->use_steno_timing_manipulation;
+		arg = strdelim(&s);
+		if (!arg || *arg == '\0')
+			fatal("%.200s line %d: Missing yes/no argument.",
+			      filename, linenum);
+		value = 0;	/* To avoid compiler warning... */
+		if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0)
+			value = 1;
+		else if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0)
+			value = 0;
+		else
+			fatal("%.200s line %d: Bad yes/no argument.", filename, linenum);
+		if (*activep && *intptr == -1)
+			*intptr = value;
+		break;
+
+
 	default:
 		fatal("process_config_line: Unimplemented opcode %d", opcode);
 	}
@@ -798,6 +818,7 @@
 	options->preferred_authentications = NULL;
 	options->bind_address = NULL;
 	options->smartcard_device = NULL;
+       options->use_steno_timing_manipulation = -1;
 	options->no_host_authentication_for_localhost = - 1;
 }
 
@@ -919,6 +940,9 @@
 		clear_forwardings(options);
 	if (options->no_host_authentication_for_localhost == - 1)
 		options->no_host_authentication_for_localhost = 0;
+	if (options->use_steno_timing_manipulation == - 1)
+		options->use_steno_timing_manipulation = 1;
+
 	/* options->proxy_command should not be set by default */
 	/* options->user will be set in the main program if appropriate */
 	/* options->hostname will be set in the main program if appropriate */
--- readconf.h	Wed Oct  3 10:39:39 2001
+++ readconf.new.h	Mon Nov 26 13:37:04 2001
@@ -9,6 +9,9 @@
  * software must be clearly marked as such, and if the derived work is
  * incompatible with the protocol description in the RFC file, it must be
  * called by a name other than "ssh" or "Secure Shell".
+ *
+ * Keystroke Timing Analysis Evasion Implementation:
+ * Copyright (c) 2001 Silicon Defense. All rights reserved. 
  */
 
 /* RCSID("$OpenBSD: readconf.h,v 1.40 2001/10/01 21:51:16 markus Exp $"); */
@@ -101,6 +104,7 @@
 	int     num_remote_forwards;
 	Forward remote_forwards[SSH_MAX_FORWARDS_PER_DIRECTION];
 	int	clear_forwardings;
+	int     use_steno_timing_manipulation;
 	int	no_host_authentication_for_localhost;
 }       Options;
 
--- servconf.c	Tue Nov 13 05:03:15 2001
+++ servconf.new.c	Mon Nov 26 13:37:04 2001
@@ -7,6 +7,9 @@
  * software must be clearly marked as such, and if the derived work is
  * incompatible with the protocol description in the RFC file, it must be
  * called by a name other than "ssh" or "Secure Shell".
+ *
+ * Keystroke Timing Analysis Evasion Implementation:
+ * Copyright (c) 2001 Silicon Defense. All rights reserved. 
  */
 
 #include "includes.h"
@@ -109,6 +112,7 @@
 	options->client_alive_count_max = -1;
 	options->authorized_keys_file = NULL;
 	options->authorized_keys_file2 = NULL;
+ 	options->use_steno_timing_manipulation = -1;
 }
 
 void
@@ -229,6 +233,8 @@
 	}
 	if (options->authorized_keys_file == NULL)
 		options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS;
+	if (options->use_steno_timing_manipulation == -1)
+	        options->use_steno_timing_manipulation = 1;
 }
 
 /* Keyword tokens. */
@@ -261,7 +267,7 @@
 	sBanner, sReverseMappingCheck, sHostbasedAuthentication,
 	sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, 
 	sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2,
-	sDeprecated 
+	sDeprecated, sUseStenoTimingManipulation 
 } ServerOpCodes;
 
 /* Textual representation of the tokens. */
@@ -334,6 +340,7 @@
 	{ "clientalivecountmax", sClientAliveCountMax },
 	{ "authorizedkeysfile", sAuthorizedKeysFile },
 	{ "authorizedkeysfile2", sAuthorizedKeysFile2 },
+	{ "usestenotimingmanipulation", sUseStenoTimingManipulation },
 	{ NULL, 0 }
 };
 
@@ -812,7 +819,6 @@
 			options->subsystem_command[options->num_subsystems] = xstrdup(arg);
 			options->num_subsystems++;
 			break;
-
 		case sMaxStartups:
 			arg = strdelim(&cp);
 			if (!arg || *arg == '\0')
@@ -865,6 +871,33 @@
 			while(arg)
 			    arg = strdelim(&cp);
 			break;
+
+		case sUseStenoTimingManipulation:
+		        intptr = &options->use_steno_timing_manipulation;
+			arg = strdelim(&cp);
+			if (!arg || *arg == '\0')
+				fatal("%s line %d: missing yes/"
+				    "/no argument for UseStenoTimingManipulation"
+				    , filename, linenum);
+			value = 0;	/* silence compiler */
+			if (strcmp(arg, "yes") == 0)
+				{value = 1;
+				debug("using steno");
+				}
+			else if (strcmp(arg, "no") == 0)
+			  {
+			    value = 0; 
+			    debug("not using steno");
+			  }
+			else
+				fatal("%s line %d: Bad yes/"
+				    "/no "
+				    "argument for UseStenoTimingManipulation: %s"
+				      , filename, linenum, arg);
+			if (*intptr == -1)
+				*intptr = value;
+			break;
+
 
 		default:
 			fatal("%s line %d: Missing handler for opcode %s (%d)",
--- servconf.h	Wed Sep 12 09:40:06 2001
+++ servconf.new.h	Mon Nov 26 13:37:04 2001
@@ -9,6 +9,9 @@
  * software must be clearly marked as such, and if the derived work is
  * incompatible with the protocol description in the RFC file, it must be
  * called by a name other than "ssh" or "Secure Shell".
+ *
+ * Keystroke Timing Analysis Evasion Implementation:
+ * Copyright (c) 2001 Silicon Defense. All rights reserved. 
  */
 
 /* RCSID("$OpenBSD: servconf.h,v 1.49 2001/08/17 18:59:47 stevesk Exp $"); */
@@ -129,6 +132,7 @@
 	char   *authorized_keys_file;	/* File containing public keys */
 	char   *authorized_keys_file2;
 	int	pam_authentication_via_kbd_int;
+	int     use_steno_timing_manipulation;
 
 }       ServerOptions;
 
--- serverloop.c	Sun Nov 11 16:06:33 2001
+++ serverloop.new.c	Mon Nov 26 13:37:04 2001
@@ -22,6 +22,9 @@
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
+ * Keystroke Timing Analysis Evasion Implementation:
+ * Copyright (c) 2001 Silicon Defense. All rights reserved. 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
@@ -186,7 +189,7 @@
  * have data or can accept data.  Optionally, a maximum time can be specified
  * for the duration of the wait (0 = infinite).
  */
-static void
+static int
 wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
     int *nallocp, u_int max_time_milliseconds)
 {
@@ -194,6 +197,9 @@
 	int ret;
 	int client_alive_scheduled = 0;
 
+	static struct timeval steno_timer = {0, 50000};
+	long int prev_timer_val = 0;
+
 	/*
 	 * if using client_alive, set the max timeout accordingly, 
 	 * and indicate that this particular timeout was for client
@@ -206,7 +212,8 @@
 	    max_time_milliseconds == 0 && options.client_alive_interval) {
 		client_alive_scheduled = 1;
 		max_time_milliseconds = options.client_alive_interval * 1000;
-	}
+	} else
+		client_alive_scheduled = 0;
 
 	/* Allocate and update select() masks for channel descriptors. */
 	channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, 0);
@@ -268,9 +275,20 @@
 	if (tvp!=NULL)
 		debug3("tvp!=NULL kid %d mili %d", child_terminated, max_time_milliseconds);
 
-	/* Wait for something to happen, or the timeout to expire. */
-	ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp);
-
+	if(options.use_steno_timing_manipulation){
+	  prev_timer_val = steno_timer.tv_usec;
+	  
+	  ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, &steno_timer);
+	  
+	  if(prev_timer_val == steno_timer.tv_usec) {
+	    debug3 ("decrementing timer forcefully");
+	    steno_timer.tv_usec -= 100;
+	  }
+	}
+	else {
+	  /* Wait for something to happen, or the timeout to expire. */
+	  ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp);
+	}
 	if (ret == -1) {
 		memset(*readsetp, 0, *nallocp);
 		memset(*writesetp, 0, *nallocp);
@@ -278,6 +296,17 @@
 			error("select: %.100s", strerror(errno));
 	} else if (ret == 0 && client_alive_scheduled)
 		client_alive_check();
+
+	if(options.use_steno_timing_manipulation) {
+	  if(steno_timer.tv_usec <= 0) {
+	    steno_timer.tv_usec = 50000;
+	    return 1;
+	  } 
+	  else
+	    return 0;
+	}
+	else 
+	  return 1;
 }
 
 /*
@@ -442,6 +471,8 @@
 	u_int previous_stdout_buffer_bytes;
 	u_int stdout_buffer_bytes;
 	int type;
+	int bogus_send_count = 0;
+	options.use_steno_timing_manipulation = 0;
 
 	debug("Entering interactive session.");
 
@@ -545,8 +576,9 @@
 
 		/* Send channel data to the client. */
 		if (packet_not_very_much_data_to_write())
-			channel_output_poll();
-
+		  {
+		    channel_output_poll(&bogus_send_count,0);
+		  }
 		/*
 		 * Bail out of the loop if the program has closed its output
 		 * descriptors, and we have no more data to send to the
@@ -688,6 +720,9 @@
 {
 	fd_set *readset = NULL, *writeset = NULL;
 	int rekeying = 0, max_fd, nalloc = 0;
+	int bogus_send_count = 0;
+	int time_out = 0;
+
 
 	debug("Entering interactive session for SSH2.");
 
@@ -706,12 +741,17 @@
 
 		rekeying = (xxx_kex != NULL && !xxx_kex->done);
 
-		if (!rekeying && packet_not_very_much_data_to_write())
-			channel_output_poll();
-		wait_until_can_do_something(&readset, &writeset, &max_fd,
-		    &nalloc, 0);
-
 		collect_children();
+
+		if (!rekeying && time_out) {
+			channel_output_poll(&bogus_send_count,
+				options.use_steno_timing_manipulation);
+			time_out = 0;
+		}
+
+		time_out = wait_until_can_do_something(&readset, &writeset,
+				 &max_fd, &nalloc, rekeying);
+
 		if (!rekeying)
 			channel_after_select(readset, writeset);
 		process_input(readset);
@@ -719,6 +759,7 @@
 			break;
 		process_output(writeset);
 	}
+
 	collect_children();
 
 	if (readset)
@@ -889,6 +930,10 @@
 			packet_put_int(c->local_window);
 			packet_put_int(c->local_maxpacket);
 			packet_send();
+			if(!options.use_steno_timing_manipulation) {
+				debug("sending no steno msg");
+				channel_request(c->remote_id,"no_steno",0);
+			}
 		}
 	} else {
 		debug("server_input_channel_open: failure %s", ctype);
--- session.c	Tue Nov 13 04:46:19 2001
+++ session.new.c	Mon Nov 26 13:37:04 2001
@@ -20,6 +20,9 @@
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
+ * Keystroke Timing Analysis Evasion Implementation:
+ * Copyright (c) 2001 Silicon Defense. All rights reserved. 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
@@ -1736,6 +1739,16 @@
 }
 
 static int
+session_no_steno_req(Session *s) {
+	packet_done();
+	debug("handling steno request");
+	if(options.use_steno_timing_manipulation) {     
+		options.use_steno_timing_manipulation = 0;
+	}
+	return 1;
+}
+
+static int
 session_shell_req(Session *s)
 {
 	packet_done();
@@ -1811,6 +1824,8 @@
 			success = session_auth_agent_req(s);
 		} else if (strcmp(rtype, "subsystem") == 0) {
 			success = session_subsystem_req(s);
+		} else if (strcmp(rtype, "no_steno") == 0) {
+			success = session_no_steno_req(s);
 		}
 	}
 	if (strcmp(rtype, "window-change") == 0) {
--- ssh.c	Sun Nov 11 15:52:04 2001
+++ ssh.new.c	Mon Nov 26 13:37:04 2001
@@ -26,6 +26,9 @@
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
+ * Keystroke Timing Analysis Evasion Implementation:
+ * Copyright (c) 2001 Silicon Defense. All rights reserved. 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
@@ -197,6 +200,8 @@
 	fprintf(stderr, "  -o 'option' Process the option as if it was read from a configuration file.\n");
 	fprintf(stderr, "  -s          Invoke command (mandatory) as SSH2 subsystem.\n");
 	fprintf(stderr, "  -b addr     Local IP address.\n");
+	fprintf(stderr, "  -S          Don't use stenographic timing manipulation\n");
+
 	exit(1);
 }
 
@@ -312,7 +317,7 @@
 
 again:
 	while ((opt = getopt(ac, av,
-	    "1246ab:c:e:fgi:kl:m:no:p:qstvxACD:F:I:L:NPR:TVX")) != -1) {
+	    "1246ab:c:e:fgi:kl:m:no:p:qstvxACSD:F:I:L:NPR:TVX")) != -1) {
 		switch (opt) {
 		case '1':
 			options.protocol = SSH_PROTO_1;
@@ -517,6 +522,11 @@
 		case 's':
 			subsystem_flag = 1;
 			break;
+
+		case 'S':
+			options.use_steno_timing_manipulation = 0;
+			break;
+
 		case 'b':
 			options.bind_address = optarg;
 			break;
@@ -1073,6 +1083,12 @@
 	if (options.forward_agent) {
 		debug("Requesting authentication agent forwarding.");
 		channel_request_start(id, "auth-agent-req at openssh.com", 0);
+		packet_send();
+	}
+
+	if (!options.use_steno_timing_manipulation) {
+		debug("sending request for no steno.");
+		channel_request_start(id, "no_steno",0);
 		packet_send();
 	}
 
--- sshd.c	Sun Nov 11 16:07:12 2001
+++ sshd.new.c	Mon Nov 26 13:37:04 2001
@@ -27,6 +27,10 @@
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
+ *
+ * Keystroke Timing Analysis Evasion Implementation:
+ * Copyright (c) 2001 Silicon Defense. All rights reserved. 
+ *
  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
@@ -564,7 +568,7 @@
 	initialize_server_options(&options);
 
 	/* Parse command-line arguments. */
-	while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:u:dDeiqtQ46")) != -1) {
+	while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:u:dDeiqtSQ46")) != -1) {
 		switch (opt) {
 		case '4':
 			IPv4or6 = AF_INET;
@@ -646,6 +650,11 @@
 		case 'u':
 			utmp_len = atoi(optarg);
 			break;
+
+		case 'S':
+			options.use_steno_timing_manipulation = 0;
+			break;
+
 		case '?':
 		default:
 			fprintf(stderr, "sshd version %s\n", SSH_VERSION);
@@ -666,6 +675,8 @@
 			fprintf(stderr, "  -u len     Maximum hostname length for utmp recording\n");
 			fprintf(stderr, "  -4         Use IPv4 only\n");
 			fprintf(stderr, "  -6         Use IPv6 only\n");
+			fprintf(stderr, "  -S         Don't use stenographic timing manipulation\n");
+
 			exit(1);
 		}
 	}


More information about the openssh-unix-dev mailing list