forked from ElementsProject/lightning
-
Notifications
You must be signed in to change notification settings - Fork 0
/
run-secret_eq_consttime.c
164 lines (132 loc) · 4.31 KB
/
run-secret_eq_consttime.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
#include "config.h"
#include <assert.h>
#include <bitcoin/privkey.c>
#include <ccan/err/err.h>
#include <ccan/time/time.h>
#include <common/setup.h>
#include <stdio.h>
#include <unistd.h>
/* AUTOGENERATED MOCKS START */
/* Generated stub for fromwire */
const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED)
{ fprintf(stderr, "fromwire called!\n"); abort(); }
/* Generated stub for towire */
void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED)
{ fprintf(stderr, "towire called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */
static bool verbose = false;
#define RUNS (16 * 10000)
static struct timerel const_time_test(struct secret *s1,
struct secret *s2,
size_t off)
{
struct timeabs start, end;
int result = 0;
memset(s1, 0, RUNS * sizeof(*s1));
memset(s2, 0, RUNS * sizeof(*s2));
for (size_t i = 0; i < RUNS; i++)
s2[i].data[off] = i;
start = time_now();
for (size_t i = 0; i < RUNS; i++)
result += secret_eq_consttime(&s1[i], &s2[i]);
end = time_now();
if (result != RUNS / 256)
errx(1, "Expected %u successes at offset %zu, not %u!",
RUNS / 256, off, result);
return time_between(end, start);
}
static inline bool secret_eq_nonconst(const struct secret *a,
const struct secret *b)
{
return memcmp(a, b, sizeof(*a)) == 0;
}
static struct timerel nonconst_time_test(struct secret *s1,
struct secret *s2,
size_t off)
{
struct timeabs start, end;
int result = 0;
memset(s1, 0, RUNS * sizeof(*s1));
memset(s2, 0, RUNS * sizeof(*s2));
for (size_t i = 0; i < RUNS; i++)
s2[i].data[off] = i;
start = time_now();
for (size_t i = 0; i < RUNS; i++)
result += secret_eq_nonconst(&s1[i], &s2[i]);
end = time_now();
if (result != RUNS / 256)
errx(1, "Expected %u successes at offset %zu, not %u!",
RUNS / 256, off, result);
return time_between(end, start);
}
static struct secret *s1, *s2;
/* Returns true if test result is expected: we consider 5% "same". */
static bool secret_time_test(struct timerel (*test)(struct secret *s1,
struct secret *s2,
size_t off),
bool should_be_const)
{
struct timerel firstbyte_time, lastbyte_time, diff;
firstbyte_time = test(s1, s2, 0);
lastbyte_time = test(s1, s2, sizeof(s1->data)-1);
if (verbose)
printf("First byte %u psec vs last byte %u psec\n",
(int)time_to_nsec(time_divide(firstbyte_time, RUNS/1000)),
(int)time_to_nsec(time_divide(lastbyte_time, RUNS/1000)));
/* If they differ by more than 5%, get upset. */
if (time_less(firstbyte_time, lastbyte_time))
diff = time_sub(lastbyte_time, firstbyte_time);
else {
/* If the lastbyte test was faster, that's a fail it we expected
* it to be slower... */
if (!should_be_const)
return false;
diff = time_sub(firstbyte_time, lastbyte_time);
}
return time_less(time_multiply(diff, 20), firstbyte_time) == should_be_const;
}
#define ITERATIONS 1000
int main(int argc, char *argv[])
{
const char *v;
int const_success, nonconst_success = ITERATIONS, i;
double load;
common_setup(argv[0]);
/* no point running this under valgrind. */
v = getenv("VALGRIND");
if (v && atoi(v) == 1)
goto exit;
s1 = calloc(RUNS, sizeof(*s1));
s2 = calloc(RUNS, sizeof(*s2));
/* When not loaded, this should pass over 50% of the time. */
const_success = 0;
for (i = 0; i < ITERATIONS; i++)
const_success += secret_time_test(const_time_test, true);
printf("=> Within 5%% %u/%u times\n", const_success, i);
/* This fails without -O2 or above, at least here (x86 Ubuntu gcc 7.3) */
if (strstr(COPTFLAGS, "-O2") || strstr(COPTFLAGS, "-O3")) {
/* Should show measurable differences at least 1/2 the time. */
nonconst_success = 0;
for (i = 0; i < 1000; i++)
nonconst_success
+= secret_time_test(nonconst_time_test, false);
printf("=> More than 5%% slower %u/%u times\n",
nonconst_success, i);
}
/* Now, check loadavg: if we weren't alone, that could explain results */
getloadavg(&load, 1);
if (load > 1.0) {
warnx("Load %.2f: too high, ignoring", load);
} else {
if (const_success < ITERATIONS / 2)
errx(1, "Only const time %u/%u?", const_success, i);
if (nonconst_success < ITERATIONS / 2)
errx(1, "memcmp seemed const time %u/%u?",
nonconst_success, i);
}
free(s1);
free(s2);
exit:
common_shutdown();
return 0;
}