rgb-cln/bitcoin/test/run-secret_eq_consttime.c

139 lines
3.4 KiB
C
Raw Normal View History

#include <assert.h>
#include <bitcoin/privkey.c>
#include <ccan/err/err.h>
#include <ccan/time/time.h>
#include <stdio.h>
/* AUTOGENERATED MOCKS START */
/* AUTOGENERATED MOCKS END */
static bool verbose = false;
#define RUNS (256 * 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);
}
/* 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 secret *s1, *s2;
struct timerel firstbyte_time, lastbyte_time, diff;
s1 = calloc(RUNS, sizeof(*s1));
s2 = calloc(RUNS, sizeof(*s2));
firstbyte_time = test(s1, s2, 0);
lastbyte_time = test(s1, s2, sizeof(s1->data)-1);
free(s1);
free(s2);
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;
}
int main(void)
{
const char *v;
int success, i;
setup_locale();
/* no point running this under valgrind. */
v = getenv("VALGRIND");
if (v && atoi(v) == 1)
exit(0);
/* I've never seen this fail more than 5 times */
success = 0;
for (i = 0; i < 10; i++)
success += secret_time_test(const_time_test, true);
printf("=> Within 5%% %u/%u times\n", success, i);
if (success < i/2)
errx(1, "Only const time %u/%u?", 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. */
success = 0;
for (i = 0; i < 10; i++)
success += secret_time_test(nonconst_time_test, false);
printf("=> More than 5%% slower %u/%u times\n", success, i);
if (success < i/2)
errx(1, "memcmp seemed const time %u/%u?", success, i);
}
return 0;
}