summaryrefslogtreecommitdiff
path: root/toxcore/crypto_core_test.cpp
blob: 8f91dce8422befd7399bc47785f9711bbcdf8145 (plain)
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
#include "crypto_core.h"

#include <algorithm>

#include <gtest/gtest.h>

namespace
{

enum {
    /**
     * The size of the arrays to compare. This was chosen to take around 2000
     * CPU clocks on x86_64.
     */
    CRYPTO_TEST_MEMCMP_SIZE = 1024 * 1024, // 1 MiB
    /**
     * The number of times we run memcmp in the test.
     *
     * We compute the median time taken to reduce error margins.
     */
    CRYPTO_TEST_MEMCMP_ITERATIONS = 500,
    /**
     * The margin of error (in clocks) we allow for this test.
     *
     * Should be within 0.5% of ~2000 CPU clocks. In reality, the code is much
     * more precise and is usually within 1 CPU clock.
     */
    CRYPTO_TEST_MEMCMP_EPS = 10,
};

clock_t memcmp_time(void *a, void *b, size_t len)
{
    clock_t start = clock();
    crypto_memcmp(a, b, len);
    return clock() - start;
}

/**
 * This function performs the actual timing. It interleaves comparison of
 * equal and non-equal arrays to reduce the influence of external effects
 * such as the machine being a little more busy 1 second later.
 */
void memcmp_median(void *src, void *same, void *not_same, size_t len,
                   clock_t *same_median, clock_t *not_same_median)
{
    clock_t same_results[CRYPTO_TEST_MEMCMP_ITERATIONS];
    clock_t not_same_results[CRYPTO_TEST_MEMCMP_ITERATIONS];

    for (size_t i = 0; i < CRYPTO_TEST_MEMCMP_ITERATIONS; i++) {
        same_results[i] = memcmp_time(src, same, len);
        not_same_results[i] = memcmp_time(src, not_same, len);
    }

    std::sort(same_results, same_results + CRYPTO_TEST_MEMCMP_ITERATIONS);
    *same_median = same_results[CRYPTO_TEST_MEMCMP_ITERATIONS / 2];
    std::sort(not_same_results, not_same_results + CRYPTO_TEST_MEMCMP_ITERATIONS);
    *not_same_median = not_same_results[CRYPTO_TEST_MEMCMP_ITERATIONS / 2];
}

/**
 * This test checks whether crypto_memcmp takes the same time for equal and
 * non-equal chunks of memory.
 */
TEST(CryptoCore, MemcmpTimingIsDataIndependent)
{
    // A random piece of memory.
    uint8_t *src = new uint8_t[CRYPTO_TEST_MEMCMP_SIZE];
    random_bytes(src, CRYPTO_TEST_MEMCMP_SIZE);

    // A separate piece of memory containing the same data.
    uint8_t *same = new uint8_t[CRYPTO_TEST_MEMCMP_SIZE];
    memcpy(same, src, CRYPTO_TEST_MEMCMP_SIZE);

    // Another piece of memory containing different data.
    uint8_t *not_same = new uint8_t[CRYPTO_TEST_MEMCMP_SIZE];
    random_bytes(not_same, CRYPTO_TEST_MEMCMP_SIZE);

    clock_t same_median;
    clock_t not_same_median;
    memcmp_median(src, same, not_same, CRYPTO_TEST_MEMCMP_SIZE, &same_median, &not_same_median);

    delete[] not_same;
    delete[] same;
    delete[] src;

    clock_t const delta = same_median > not_same_median
                          ? same_median - not_same_median
                          : not_same_median - same_median;

    EXPECT_LT(delta, CRYPTO_TEST_MEMCMP_EPS)
            << "Delta time is too long (" << delta << " >= " << CRYPTO_TEST_MEMCMP_EPS << ")\n"
            << "Time of the same data comparation: " << same_median << " clocks\n"
            << "Time of the different data comparation: " << not_same_median << " clocks";
}

}  // namespace