]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
updated test
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Sun, 27 Jan 2013 18:19:13 +0000 (19:19 +0100)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Sun, 27 Jan 2013 18:19:13 +0000 (19:19 +0100)
tests/suite/mini-record-timing.c

index 6363ff226d14dd39bd362aea0f0fa8e60bfcb281..dcfe69abe09463a90a6e06c4a2d518d26d34b0c3 100644 (file)
@@ -65,7 +65,7 @@ client_log_func (int level, const char *str)
 }
 #endif
 
-static void terminate(void);
+#undef REHANDSHAKE
 
 /* This program tests the robustness of record
  * decoding.
@@ -107,28 +107,31 @@ const gnutls_datum_t server_key = { server_key_pem,
 /* A very basic TLS client, with anonymous authentication.
  */
 
-#define MAX_BUF 1024
 
-#define MAX_PER_POINT (16*1024)
-#define MAX_MEASUREMENTS (MAX_PER_POINT*(sizeof(points)/sizeof(points[0])))
+#define MAX_PER_POINT (684*1024)
+#define MAX_MEASUREMENTS(np) (MAX_PER_POINT*(np))
+#define MAX_BUF 1024
 
 struct point_st {
   unsigned char byte1;
   unsigned char byte2;
   unsigned midx;
-  unsigned long measurements[MAX_PER_POINT];
+  unsigned long *measurements;
+  unsigned long *smeasurements;
 };
 
-static struct point_st points[] = {
-  { 0, 0, 0 },
-//  { 1, 1, 0 },
-//  { 14, 14, 0 },
-  { 253, 253, 0 },
+struct test_st {
+  struct point_st* points;
+  unsigned int npoints;
+  const char* desc;
+  const char* file;
+  const char* name;
+  unsigned text_size;
 };
 
-struct point_st *prev_point_ptr = &points[0];
+struct point_st *prev_point_ptr = NULL;
 unsigned int point_idx = 0;
-
+static gnutls_session_t cli_session = NULL;
 
 static ssize_t
 push(gnutls_transport_ptr_t tr, const void *_data, size_t len)
@@ -143,27 +146,32 @@ push_crippled (gnutls_transport_ptr_t tr, const void *_data, size_t len)
 {
 int fd = (long int)tr;
 unsigned char* data = (void*)_data;
-struct point_st * p = &points[point_idx];
+struct point_st * p;
+unsigned p_size;
+struct test_st * test = gnutls_session_get_ptr(cli_session);
+  
+  p = &test->points[point_idx];
+  p_size = test->npoints;
 
   memcpy(&data[len-32], data+5, 32);
 
-//fprintf(stderr, "sending: %d.%d: %d\n", (unsigned)p->byte1, (unsigned)p->byte2, (int)len);
+/*fprintf(stderr, "sending: %d: %d\n", (unsigned)p->byte1, (int)len);*/
   data[len-17] ^= p->byte1;
   data[len-18] ^= p->byte2;
 
   prev_point_ptr = p;
   point_idx++;
-  if (point_idx >= (sizeof(points)/sizeof(points[0])))
+  if (point_idx >= p_size)
     point_idx = 0;
     
   return send(fd, data, len, 0);
 }
 
 
-static unsigned long timespec_sub_us(struct timespec *a, struct timespec *b)
+static unsigned long timespec_sub_ns(struct timespec *a, struct timespec *b)
 {
-  return (a->tv_sec*1000*1000 + a->tv_nsec/1000 - (b->tv_sec *1000*1000 + 
-          b->tv_nsec/1000));
+  return (a->tv_sec*1000*1000*1000 + a->tv_nsec - (b->tv_sec*1000*1000*1000 + 
+          b->tv_nsec));
 }
 
 static
@@ -171,11 +179,13 @@ double calc_avg(unsigned long *diffs, unsigned int diffs_size)
 {
 double avg = 0;
 unsigned int i;
+unsigned int start = diffs_size/20;
+unsigned int stop = diffs_size-diffs_size/20;
 
-  for(i=0;i<diffs_size;i++)
+  for(i=start;i<stop;i++)
     avg += diffs[i];
     
-  avg /= diffs_size;
+  avg /= (stop-start);
 
   return avg;
 }
@@ -200,20 +210,43 @@ double calc_median(unsigned long *diffs, unsigned int diffs_size)
 {
 double med;
 
-  qsort(diffs, diffs_size, sizeof(diffs[0]), compar);
-    
-  med = diffs[diffs_size/2];
+  if (diffs_size % 2 == 1)
+    med = diffs[diffs_size/2];
+  else
+    {
+      med = diffs[diffs_size/2] + diffs[(diffs_size-1)/2];
+      med /= 2;
+    }
 
   return med;
 }
 
+static
+unsigned long calc_min(unsigned long *diffs, unsigned int diffs_size)
+{
+unsigned long min = 0, i;
+unsigned int start = diffs_size/20;
+unsigned int stop = diffs_size-diffs_size/20;
+
+
+  for (i=start;i<stop;i++) {
+    if (min == 0)
+      min = diffs[i];
+    else if (diffs[i] < min)
+      min = diffs[i];
+  }
+  return min;
+}
+
 static
 double calc_var(unsigned long *diffs, unsigned int diffs_size, double avg)
 {
 double sum = 0, d;
 unsigned int i;
+unsigned int start = diffs_size/20;
+unsigned int stop = diffs_size-diffs_size/20;
 
-  for (i=0;i<diffs_size;i++) {
+  for (i=start;i<stop;i++) {
     d = ((double)diffs[i] - avg);
     d *= d;
     
@@ -226,7 +259,7 @@ unsigned int i;
 
 
 static void
-client (int fd, const char* prio, unsigned int text_size)
+client (int fd, const char* prio, unsigned int text_size, struct test_st *test)
 {
   int ret;
   char buffer[MAX_BUF + 1];
@@ -234,15 +267,11 @@ client (int fd, const char* prio, unsigned int text_size)
   gnutls_certificate_credentials_t x509_cred;
   gnutls_session_t session;
   struct timespec start, stop;
-  static unsigned int taken = 0;
-  static unsigned long *measurements;
-  static unsigned long min = 0, max = 0;
+  static unsigned long taken = 0;
+  static unsigned long measurement;
+  static unsigned long min = 0;
   const char* err;
 
-  measurements = malloc(sizeof(measurements[0])*MAX_MEASUREMENTS);
-  if (measurements == NULL)
-    exit(1);
-
   gnutls_global_init ();
   
   setpriority(PRIO_PROCESS, getpid(), -15);
@@ -256,14 +285,19 @@ client (int fd, const char* prio, unsigned int text_size)
 
   gnutls_certificate_allocate_credentials (&x509_cred);
 
+#ifdef REHANDSHAKE
+restart:
+#endif
+
   /* Initialize TLS session
    */
-restart:
   gnutls_init (&session, GNUTLS_CLIENT);
+  gnutls_session_set_ptr(session, test);
+  cli_session = session;
 
   /* Use default priorities */
-  if (gnutls_priority_set_direct (session, prio, &err) < 0) {
-    fprintf(stderr, "Error in %s\n", err);
+  if ((ret=gnutls_priority_set_direct (session, prio, &err)) < 0) {
+    fprintf(stderr, "Error in priority string %s: %s\n", gnutls_strerror(ret), err);
     exit(1);
   }
 
@@ -286,14 +320,19 @@ restart:
       gnutls_perror (ret);
       exit(1);
     }
-    
-  if (gnutls_protocol_get_version(session) != GNUTLS_TLS1_1)
+   
+  ret = gnutls_protocol_get_version(session);
+  if (ret < GNUTLS_TLS1_1)
     {
-      fprintf (stderr, "client: Handshake didn't negotiate TLS 1.1\n");
+      fprintf (stderr, "client: Handshake didn't negotiate TLS 1.1 (or later)\n");
       exit(1);
     }
 
   gnutls_transport_set_push_function (session, push_crippled);
+
+#ifndef REHANDSHAKE
+restart:
+#endif
   do {
     ret = gnutls_record_send (session, text, sizeof(text));
   } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
@@ -311,23 +350,24 @@ restart:
   } while(ret == -1 && errno == EAGAIN);
 #endif
 
-  if (taken < MAX_MEASUREMENTS)
+  if (taken < MAX_MEASUREMENTS(test->npoints) && ret > 0)
     {
       gettime(&stop);
-      measurements[taken] = timespec_sub_us(&stop, &start);
+      taken++;
+      measurement = timespec_sub_ns(&stop, &start);
+      prev_point_ptr->measurements[prev_point_ptr->midx] = measurement;
 
-      if (min > measurements[taken] || min == 0)
-        min = measurements[taken];
-      if (max < measurements[taken])
-        max = measurements[taken];
+/*fprintf(stderr, "(%u,%u): %lu\n", (unsigned) prev_point_ptr->byte1,
+ (unsigned) prev_point_ptr->byte2, measurements[taken]);*/
+      memcpy(&measurement, buffer, sizeof(measurement));
+      prev_point_ptr->smeasurements[prev_point_ptr->midx] = measurement;
+      prev_point_ptr->midx++;
 
-//fprintf(stderr, "(%u,%u): %lu\n", (unsigned) prev_point_ptr->byte1,
-// (unsigned) prev_point_ptr->byte2, measurements[taken]);
-      prev_point_ptr->measurements[prev_point_ptr->midx++] = measurements[taken];
-      taken++;
+      /* read server's measurement */
       
+#ifdef REHANDSHAKE
       gnutls_deinit(session);
-      
+#endif      
       goto restart;
     }
 #ifndef TLS_RECV
@@ -343,38 +383,48 @@ restart:
   gnutls_bye (session, GNUTLS_SHUT_WR);
   
   {
-    double avg1, avg2, var, med;
+    double avg2, var, med, savg, smed;
     unsigned i;
+    FILE* fp = NULL;
     
-    printf("Taken %u measurements\n", taken);
-    avg1 = calc_avg(measurements, taken);
-    med = calc_median(measurements, taken);
-    
-    var = calc_var(measurements, taken, avg1);
+    if (test->file)
+      fp = fopen(test->file, "w");
     
-    printf("Average processing time: %.3f microsec, Median: %.3f Variance: %.3f\n", avg1, med, var);
-    printf("(Min,Max)=(%lu,%lu)\n\n", min, max);
+    if (fp) /* point, avg, median */
+     fprintf(fp, "Delta,TimeAvg,TimeMedian,ServerAvg,ServerMedian\n");
 
-    for (i=0;i<sizeof(points)/sizeof(points[0]);i++)
+    for (i=0;i<test->npoints;i++)
       {
-        avg2 = calc_avg( points[i].measurements, points[i].midx);
-        var = calc_var(points[i].measurements, points[i].midx, avg2);
-        med = calc_median(points[i].measurements, points[i].midx);
-        printf("(%u,%u) Avg: %.3f microsec, Median: %.3f, Variance: %.3f\n", (unsigned)points[i].byte2, (unsigned)points[i].byte1, 
-               avg2, med, var);
-        if (i > 0)
-          {
-            printf("Avg diff (%u,%u)-(%u,%u)=%.3f\n", 
-                   (unsigned)points[0].byte2, (unsigned)points[0].byte1,
-                   (unsigned)points[i].byte2, (unsigned)points[i].byte1,
-                   avg1-avg2);
-          
-          }
-        else
-          avg1 = avg2;
+        qsort( test->points[i].measurements, test->points[i].midx, 
+               sizeof(test->points[i].measurements[0]), compar);
+
+        qsort( test->points[i].smeasurements, test->points[i].midx, 
+               sizeof(test->points[i].smeasurements[0]), compar);
+     
+        avg2 = calc_avg( test->points[i].measurements, test->points[i].midx);
+        /*var = calc_var( test->points[i].measurements, test->points[i].midx, avg2);*/
+        med = calc_median( test->points[i].measurements, test->points[i].midx);
+
+        savg = calc_avg( test->points[i].smeasurements, test->points[i].midx);
+        /*var = calc_var( test->points[i].measurements, test->points[i].midx, avg2);*/
+        smed = calc_median( test->points[i].smeasurements, test->points[i].midx);
+        /*min = calc_min( test->points[i].measurements, test->points[i].midx);*/
+        
+        if (fp) /* point, avg, median */
+          fprintf(fp, "%u,%.2lf,%.2lf,%.2lf,%.2lf\n", (unsigned)test->points[i].byte1, 
+                  avg2,med,savg, smed);
+        
+        /*printf("(%u) Avg: %.3f nanosec, Median: %.3f, Variance: %.3f\n", (unsigned)test->points[i].byte1, 
+               avg2, med, var);*/
       }
+
+    if (fp)
+      fclose(fp);
   }
 
+  if (test->desc)
+    fprintf(stderr, "Description: %s\n", test->desc);
+
   close (fd);
 
   gnutls_deinit (session);
@@ -385,14 +435,6 @@ restart:
 }
 
 
-/* These are global */
-pid_t child;
-
-static void terminate(void)
-{
-  kill(child, SIGTERM);
-  exit(1);
-}
 
 static void
 server (int fd, const char* prio)
@@ -402,6 +444,8 @@ char buffer[MAX_BUF + 1];
 gnutls_session_t session;
 gnutls_certificate_credentials_t x509_cred;
 const char* err;
+struct timespec start, stop;
+static unsigned long measurement;
 
   setpriority(PRIO_PROCESS, getpid(), -15);
 
@@ -421,18 +465,20 @@ const char* err;
   if (ret < 0)
     {
       fprintf(stderr, "Could not set certificate\n");
-      terminate();
+      return;
     }
 
+#ifdef REHANDSHAKE
 restart:
+#endif
   gnutls_init (&session, GNUTLS_SERVER);
 
   /* avoid calling all the priority functions, since the defaults
    * are adequate.
    */
-  if (gnutls_priority_set_direct (session, prio, &err) < 0) {
-    fprintf(stderr, "Error in %s\n", err);
-    terminate();
+  if ((ret=gnutls_priority_set_direct (session, prio, &err)) < 0) {
+    fprintf(stderr, "Error in priority string %s: %s\n", gnutls_strerror(ret), err);
+    return;
   }
 
   gnutls_credentials_set (session, GNUTLS_CRD_CERTIFICATE, x509_cred);
@@ -451,26 +497,41 @@ restart:
       if (ret != GNUTLS_E_UNEXPECTED_PACKET_LENGTH)
 #endif
         {
-          close (fd);
-          gnutls_deinit (session);
           fprintf( stderr, "server: Handshake has failed (%s)\n\n", gnutls_strerror (ret));
-          terminate();
         }
       goto finish;
     }
 
+#ifndef REHANDSHAKE
+restart:
+#endif
+
+  clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &start);
   do {
     ret = gnutls_record_recv (session, buffer, sizeof (buffer));
   } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
 
-  if (ret < 0)
+  clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &stop);
+
+  if (ret == GNUTLS_E_DECRYPTION_FAILED)
     {
+      gnutls_session_force_valid(session);
+      measurement = timespec_sub_ns(&stop, &start);
       do {
-        ret = gnutls_alert_send(session, GNUTLS_AL_FATAL, GNUTLS_A_BAD_RECORD_MAC);
+        ret = gnutls_record_send(session, &measurement, sizeof(measurement));
+        /* GNUTLS_AL_FATAL, GNUTLS_A_BAD_RECORD_MAC); */
       } while (ret == GNUTLS_E_AGAIN || ret == GNUTLS_E_INTERRUPTED);
+
+#ifdef REHANDSHAKE
       gnutls_deinit(session);
-      goto restart;
+#endif
+      if (ret >= 0)
+        goto restart;
     }
+  else if (ret < 0)
+    fprintf(stderr, "err: %s\n", gnutls_strerror(ret));
+  
 
   /* do not wait for the peer to close the connection.
    */
@@ -483,13 +544,13 @@ finish:
   gnutls_certificate_free_credentials (x509_cred);
 
   gnutls_global_deinit ();
-
 }
 
-static void start (const char* prio, unsigned int text_size)
+static void start (const char* prio, unsigned int text_size, struct test_st *p)
 {
   int fd[2];
   int ret;
+  pid_t child;
   
   ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
   if (ret < 0)
@@ -506,17 +567,17 @@ static void start (const char* prio, unsigned int text_size)
       exit(1);
     }
 
-  if (child)
+  if (child != 0)
     {
       /* parent */
       close(fd[1]);
       server (fd[0], prio);
       kill(child, SIGTERM);
     }
-  else 
+  else if (child == 0)
     {
       close(fd[0]);
-      client (fd[1], prio, text_size);
+      client (fd[1], prio, text_size, p);
       exit(0);
     }
 }
@@ -532,24 +593,214 @@ int status;
         fprintf(stderr, "Child died with sigsegv\n");
       else
         fprintf(stderr, "Child died with status %d\n", WEXITSTATUS(status));
-      terminate();
     }
   return;
 }
 
+static struct point_st all_points[256];
+static struct point_st all_points_one[256];
+
+
+#define NPOINTS(points) (sizeof(points)/sizeof(points[0]))
+
+/* Test that outputs a graph of the timings
+ * when manipulating the last record byte (pad)
+ * for AES-SHA1.
+ */
+static struct test_st test_sha1 = {
+  .points = all_points,
+  .npoints = NPOINTS(all_points),
+  .text_size = 18*16,
+  .name = "sha1",
+  .file = "out-sha1.txt",
+  .desc = NULL
+};
+
+/* Test that outputs a graph of the timings
+ * when manipulating the last record byte (pad)
+ * for AES-SHA256.
+ */
+static struct test_st test_sha256 = {
+  .points = all_points,
+  .npoints = NPOINTS(all_points),
+  .text_size = 17*16,
+  .name = "sha256",
+  .file = "out-sha256.txt",
+  .desc = NULL
+};
+
+/* Test that outputs a graph of the timings
+ * when manipulating the last record byte (pad)
+ * for AES-SHA1, on a short message.
+ */
+static struct test_st test_sha1_short = {
+  .points = all_points,
+  .npoints = NPOINTS(all_points),
+  .text_size = 16*2,
+  .name = "sha1-short",
+  .file = "out-sha1-short.txt",
+  .desc = NULL
+};
+
+/* Test that outputs a graph of the timings
+ * when manipulating the last record byte (pad)
+ * for AES-SHA256.
+ */
+static struct test_st test_sha256_short = {
+  .points = all_points,
+  .npoints = NPOINTS(all_points),
+  .text_size = 16*2,
+  .name = "sha256-short",
+  .file = "out-sha256-short.txt",
+  .desc = NULL
+};
+
+/* Test that outputs a graph of the timings
+ * when manipulating the last record byte (pad)
+ * for AES-SHA1, on a short message and
+ * the byte before the last is set to one.
+ * (i.e. we want to see whether the padding
+ *  [1,1] shows up in the measurements)
+ */
+static struct test_st test_sha1_one = {
+  .points = all_points_one,
+  .npoints = NPOINTS(all_points_one),
+  .text_size = 16*2,
+  .name = "sha1-one",
+  .file = "out-sha1-one.txt",
+  .desc = NULL
+};
+
 int main(int argc, char** argv)
 {
+unsigned int i;
+struct test_st* test;
+const char* hash;
+char prio[512];
+
   signal(SIGCHLD, ch_handler);
+  signal(SIGPIPE, SIG_IGN);
 
-  printf("\nAES-SHA1 (GnuTLS attack)\n");
-  start("NONE:+COMP-NULL:+AES-128-CBC:+SHA1:+RSA:%COMPAT:+VERS-TLS1.1:+VERS-TLS1.0:+VERS-SSL3.0", 18*16);
-//  start("PERFORMANCE:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1:%COMPAT", 18*16);
+  if (argc > 1)
+    {
+      if (strcmp(argv[1], "sha1")== 0)
+        {
+          test = &test_sha1;
+          hash = "SHA1";
+        }
+      else if (strncmp(argv[1], "sha2", 4)== 0)
+        {
+          test = &test_sha256;
+          hash = "SHA256";
+        }
+      else if (strcmp(argv[1], "sha1-short")== 0)
+        {
+          test = &test_sha1_short;
+          hash = "SHA1";
+        }
+      else if (strcmp(argv[1], "sha256-short")== 0)
+        {
+          test = &test_sha256_short;
+          hash = "SHA256";
+        }
+      else if (strcmp(argv[1], "sha1-one")== 0)
+        {
+          test = &test_sha1_one;
+          hash = "SHA1";
+        }
+      else 
+        {
+          fprintf(stderr, "Unknown test: %s\n", argv[1]);
+          exit(1);
+        }
+    }
+  else
+    {
+      fprintf(stderr, "Please specify the test, sha1, sha1-one, sha256, sha1-short, sha256-short\n");
+      exit(1);
+    }
 
-//  printf("\nAES-SHA1 (full plaintext recovery)\n");
-//  start("PERFORMANCE:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA1:%COMPAT", 2*16);
+  memset(&all_points, 0, sizeof(all_points));
+  for (i=0;i<256;i++)
+    {
+      all_points[i].byte1 = i;
+      all_points[i].measurements = malloc(MAX_PER_POINT*sizeof(all_points[i].measurements[0]));
+      all_points[i].smeasurements = malloc(MAX_PER_POINT*sizeof(all_points[i].measurements[0]));
+    }
+    
+  memset(&all_points_one, 0, sizeof(all_points_one));
+  for (i=0;i<256;i++)
+    {
+      all_points_one[i].byte1 = i;
+      all_points_one[i].byte2 = 1;
+      all_points_one[i].measurements = all_points[i].measurements;
+      all_points_one[i].smeasurements = all_points[i].smeasurements;
+    }
+    
 
-//  printf("\nAES-SHA256\n");
-//  start("PERFORMANCE:-CIPHER-ALL:+AES-128-CBC:-MAC-ALL:+SHA256:%COMPAT");
+  remove(test->file);
+  snprintf(prio, sizeof(prio), "NONE:+COMP-NULL:+AES-128-CBC:+%s:+RSA:%%COMPAT:+VERS-TLS1.2:+VERS-TLS1.1", hash);
+
+  printf("\nAES-%s (calculating different padding timings)\n", hash);
+  start(prio, test->text_size, test);
+
+  signal(SIGCHLD, SIG_IGN);
+  
+#ifdef PDF
+  snprintf(prio, sizeof(prio),
+         "R -e 'pdf(file=\"%s-timings-avg.pdf\");z=read.csv(\"%s\");"
+         "plot(z$Delta,z$TimeAvg,xlab=\"Delta\",ylab=\"Average timings (ns)\");"
+         "dev.off();'"
+         test->name, test->file);
+  system(prio);
+
+  snprintf(prio, sizeof(prio),
+         "R -e 'pdf(file=\"%s-timings-med.pdf\");z=read.csv(\"%s\");"
+         "plot(z$Delta,z$TimeMedian,xlab=\"Delta\",ylab=\"Median timings (ns)\");"
+         "dev.off();'";
+         test->name, test->file);
+  system(prio);
+
+  snprintf(prio, sizeof(prio),
+         "R -e 'pdf(file=\"%s-server-timings-avg.pdf\");z=read.csv(\"%s\");"
+         "plot(z$Delta,z$ServerAvg,xlab=\"Delta\",ylab=\"Average timings (ns)\");"
+         "dev.off();'"
+         test->name, test->file);
+  system(prio);
+
+  snprintf(prio, sizeof(prio),
+         "R -e 'pdf(file=\"%s-server-timings-med.pdf\");z=read.csv(\"%s\");"
+         "plot(z$Delta,z$ServerMedian,xlab=\"Delta\",ylab=\"Median timings (ns)\");"
+         "dev.off();'";
+         test->name, test->file);
+  system(prio);
+#else
+  snprintf(prio, sizeof(prio),
+         "R -e 'z=read.csv(\"%s\");png(filename = \"%s-timings-avg.png\",width=1024,height=1024,units=\"px\","
+         "bg=\"white\");plot(z$Delta,z$TimeAvg,xlab=\"Delta\",ylab=\"Average timings (ns)\");dev.off();'",
+         test->file, test->name);
+  system(prio);
+
+  snprintf(prio, sizeof(prio),
+         "R -e 'z=read.csv(\"%s\");"
+         "png(filename = \"%s-timings-med.png\",width=1024,height=1024,units=\"px\","
+         "bg=\"white\");plot(z$Delta,z$TimeMedian,xlab=\"Delta\",ylab=\"Median timings (ns)\");dev.off();'",
+         test->file, test->name);
+  system(prio);
+
+  snprintf(prio, sizeof(prio),
+         "R -e 'z=read.csv(\"%s\");png(filename = \"%s-server-timings-avg.png\",width=1024,height=1024,units=\"px\","
+         "bg=\"white\");plot(z$Delta,z$ServerAvg,xlab=\"Delta\",ylab=\"Average timings (ns)\");dev.off();'",
+         test->file, test->name);
+  system(prio);
+
+  snprintf(prio, sizeof(prio),
+         "R -e 'z=read.csv(\"%s\");"
+         "png(filename = \"%s-server-timings-med.png\",width=1024,height=1024,units=\"px\","
+         "bg=\"white\");plot(z$Delta,z$ServerMedian,xlab=\"Delta\",ylab=\"Median timings (ns)\");dev.off();'",
+         test->file, test->name);
+  system(prio);
+#endif
 }
 
 #endif /* _WIN32 */