}
#endif
-static void terminate(void);
+#undef REHANDSHAKE
/* This program tests the robustness of record
* decoding.
/* 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)
{
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
{
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;
}
{
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;
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];
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);
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);
}
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);
} 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
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);
}
-/* These are global */
-pid_t child;
-
-static void terminate(void)
-{
- kill(child, SIGTERM);
- exit(1);
-}
static void
server (int fd, const char* prio)
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);
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);
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.
*/
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)
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);
}
}
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 */