core-lightning/bitcoin/test/run-secret_eq_consttime.c
Rusty Russell cfb320c972 wire: move remaining bitcoin functions out to bitcoin/ files.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
2020-05-18 14:51:12 +02:00

158 lines
4.2 KiB
C

#include <assert.h>
#include <bitcoin/privkey.c>
#include <ccan/err/err.h>
#include <ccan/time/time.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(void)
{
const char *v;
int const_success, nonconst_success = ITERATIONS, i;
double load;
setup_locale();
/* no point running this under valgrind. */
v = getenv("VALGRIND");
if (v && atoi(v) == 1)
exit(0);
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)
errx(0, "Load %.2f: too high, ignoring", load);
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);
return 0;
}