-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Copy pathfixture.c
184 lines (157 loc) · 5.45 KB
/
fixture.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
/** dude, is my code constant time?
*
* This file measures the execution time of a given function many times with
* different inputs and performs a Welch's t-test to determine if the function
* runs in constant time or not. This is essentially leakage detection, and
* not a timing attack.
*
* Notes:
*
* - the execution time distribution tends to be skewed towards large
* timings, leading to a fat right tail. Most executions take little time,
* some of them take a lot. We try to speed up the test process by
* throwing away those measurements with large cycle count. (For example,
* those measurements could correspond to the execution being interrupted
* by the OS.) Setting a threshold value for this is not obvious; we just
* keep the x% percent fastest timings, and repeat for several values of x.
*
* - the previous observation is highly heuristic. We also keep the uncropped
* measurement time and do a t-test on that.
*
* - we also test for unequal variances (second order test), but this is
* probably redundant since we're doing as well a t-test on cropped
* measurements (non-linear transform)
*
* - as long as any of the different test fails, the code will be deemed
* variable time.
*/
#include <assert.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../console.h"
#include "../random.h"
#include "constant.h"
#include "fixture.h"
#include "plot.h"
#include "ttest.h"
static t_context_t *t;
/* threshold values for Welch's t-test */
enum {
t_threshold_bananas = 500, /* Test failed with overwhelming probability */
t_threshold_moderate = 10, /* Test failed */
};
static void __attribute__((noreturn)) die(void)
{
exit(111);
}
static void differentiate(int64_t *exec_times,
const int64_t *before_ticks,
const int64_t *after_ticks)
{
for (size_t i = 0; i < N_MEASURES; i++)
exec_times[i] = after_ticks[i] - before_ticks[i];
}
static void update_statistics(const int64_t *exec_times, uint8_t *classes)
{
for (size_t i = 0; i < N_MEASURES; i++) {
int64_t difference = exec_times[i];
/* CPU cycle counter overflowed or dropped measurement */
if (difference <= 0)
continue;
/* do a t-test on the execution time */
t_push(t, difference, classes[i]);
}
}
static bool report(void)
{
double max_t = fabs(t_compute(t));
double number_traces_max_t = t->n[0] + t->n[1];
double max_tau = max_t / sqrt(number_traces_max_t);
add_data(max_t);
printf("\033[A\033[2K");
printf("meas: %7.2lf M, ", (number_traces_max_t / 1e6));
if (number_traces_max_t < ENOUGH_MEASURE) {
printf("not enough measurements (%.0f still to go).\n",
ENOUGH_MEASURE - number_traces_max_t);
return false;
}
/* max_t: the t statistic value
* max_tau: a t value normalized by sqrt(number of measurements).
* this way we can compare max_tau taken with different
* number of measurements. This is sort of "distance
* between distributions", independent of number of
* measurements.
* (5/tau)^2: how many measurements we would need to barely
* detect the leak, if present. "barely detect the
* leak" = have a t value greater than 5.
*/
printf("max t: %+7.2f, max tau: %.2e, (5/tau)^2: %.2e.\n", max_t, max_tau,
(double) (5 * 5) / (double) (max_tau * max_tau));
/* Definitely not constant time */
if (max_t > t_threshold_bananas)
return false;
/* Probably not constant time. */
if (max_t > t_threshold_moderate)
return false;
/* For the moment, maybe constant time. */
return true;
}
static bool doit(int mode)
{
int64_t *before_ticks = calloc(N_MEASURES + 1, sizeof(int64_t));
int64_t *after_ticks = calloc(N_MEASURES + 1, sizeof(int64_t));
int64_t *exec_times = calloc(N_MEASURES, sizeof(int64_t));
uint8_t *classes = calloc(N_MEASURES, sizeof(uint8_t));
uint8_t *input_data = calloc(N_MEASURES * CHUNK_SIZE, sizeof(uint8_t));
if (!before_ticks || !after_ticks || !exec_times || !classes ||
!input_data) {
die();
}
prepare_inputs(input_data, classes);
bool ret = measure(before_ticks, after_ticks, input_data, mode);
differentiate(exec_times, before_ticks, after_ticks);
update_statistics(exec_times, classes);
ret &= report();
free(before_ticks);
free(after_ticks);
free(exec_times);
free(classes);
free(input_data);
return ret;
}
static void init_once(void)
{
init_dut();
t_init(t);
}
static bool test_const(char *text, int mode)
{
bool result = false;
t = malloc(sizeof(t_context_t));
init_data_buffer();
for (int cnt = 0; cnt < TEST_TRIES; ++cnt) {
printf("Testing %s...(%d/%d)\n\n", text, cnt, TEST_TRIES);
init_once();
for (int i = 0; i < ENOUGH_MEASURE / (N_MEASURES - DROP_SIZE * 2) + 1;
++i)
result = doit(mode);
printf("\033[A\033[2K\033[A\033[2K");
next_try();
if (result)
break;
}
plot_graph(t_threshold_moderate, text);
free(t);
return result;
}
#define DUT_FUNC_IMPL(op) \
bool is_##op##_const(void) \
{ \
return test_const(#op, DUT(op)); \
}
#define _(x) DUT_FUNC_IMPL(x)
DUT_FUNCS
#undef _