You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
157 lines
4.2 KiB
157 lines
4.2 KiB
#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;
|
|
}
|
|
|