Coverage Report

Created: 2022-12-08 06:10

/src/libgcrypt/random/jitterentropy-gcd.c
Line
Count
Source (jump to first uncovered line)
1
/* Jitter RNG: GCD health test
2
 *
3
 * Copyright (C) 2021, Joshua E. Hill <josh@keypair.us>
4
 * Copyright (C) 2021, Stephan Mueller <smueller@chronox.de>
5
 *
6
 * License: see LICENSE file in root directory
7
 *
8
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
9
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
10
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
11
 * WHICH ARE HEREBY DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE
12
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
13
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
14
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
15
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
16
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
17
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
18
 * USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
19
 * DAMAGE.
20
 */
21
22
#include "jitterentropy.h"
23
#include "jitterentropy-gcd.h"
24
25
/* The common divisor for all timestamp deltas */
26
static uint64_t jent_common_timer_gcd = 0;
27
28
static inline int jent_gcd_tested(void)
29
0
{
30
0
  return (jent_common_timer_gcd != 0);
31
0
}
32
33
/* A straight forward implementation of the Euclidean algorithm for GCD. */
34
static inline uint64_t jent_gcd64(uint64_t a, uint64_t b)
35
0
{
36
  /* Make a greater a than or equal b. */
37
0
  if (a < b) {
38
0
    uint64_t c = a;
39
0
    a = b;
40
0
    b = c;
41
0
  }
42
43
  /* Now perform the standard inner-loop for this algorithm.*/
44
0
  while (b != 0) {
45
0
    uint64_t r;
46
47
0
    r = a % b;
48
49
0
    a = b;
50
0
    b = r;
51
0
  }
52
53
0
  return a;
54
0
}
55
56
static int jent_gcd_analyze_internal(uint64_t *delta_history, size_t nelem,
57
             uint64_t *running_gcd_out,
58
             uint64_t *delta_sum_out)
59
0
{
60
0
  uint64_t running_gcd, delta_sum = 0;
61
0
  size_t i;
62
63
0
  if (!delta_history)
64
0
    return -EAGAIN;
65
66
0
  running_gcd = delta_history[0];
67
68
  /* Now perform the analysis on the accumulated delta data. */
69
0
  for (i = 1; i < nelem; i++) {
70
    /*
71
     * ensure that we have a varying delta timer which is necessary
72
     * for the calculation of entropy -- perform this check
73
     * only after the first loop is executed as we need to prime
74
     * the old_data value
75
     */
76
0
    if (delta_history[i] >= delta_history[i - 1])
77
0
      delta_sum +=  delta_history[i] - delta_history[i - 1];
78
0
    else
79
0
      delta_sum +=  delta_history[i - 1] - delta_history[i];
80
81
    /*
82
     * This calculates the gcd of all the delta values. that is
83
     * gcd(delta_1, delta_2, ..., delta_nelem)
84
85
     * Some timers increment by a fixed (non-1) amount each step.
86
     * This code checks for such increments, and allows the library
87
     * to output the number of such changes have occurred.
88
     */
89
0
    running_gcd = jent_gcd64(delta_history[i], running_gcd);
90
0
  }
91
92
0
  *running_gcd_out = running_gcd;
93
0
  *delta_sum_out = delta_sum;
94
95
0
  return 0;
96
0
}
97
98
int jent_gcd_analyze(uint64_t *delta_history, size_t nelem)
99
0
{
100
0
  uint64_t running_gcd, delta_sum;
101
0
  int ret = jent_gcd_analyze_internal(delta_history, nelem, &running_gcd,
102
0
              &delta_sum);
103
104
0
  if (ret == -EAGAIN)
105
0
    return 0;
106
107
  /*
108
   * Variations of deltas of time must on average be larger than 1 to
109
   * ensure the entropy estimation implied with 1 is preserved.
110
   */
111
0
  if (delta_sum <= nelem - 1) {
112
0
    ret = EMINVARVAR;
113
0
    goto out;
114
0
  }
115
116
  /*
117
   * Ensure that we have variations in the time stamp below 100 for at
118
   * least 10% of all checks -- on some platforms, the counter increments
119
   * in multiples of 100, but not always
120
   */
121
0
  if (running_gcd >= 100) {
122
0
    ret = ECOARSETIME;
123
0
    goto out;
124
0
  }
125
126
  /*  Adjust all deltas by the observed (small) common factor. */
127
0
  if (!jent_gcd_tested())
128
0
    jent_common_timer_gcd = running_gcd;
129
130
0
out:
131
0
  return ret;
132
0
}
133
134
uint64_t *jent_gcd_init(size_t nelem)
135
0
{
136
0
  uint64_t *delta_history;
137
138
0
  delta_history = jent_zalloc(nelem * sizeof(uint64_t));
139
0
  if (!delta_history)
140
0
    return NULL;
141
142
0
  return delta_history;
143
0
}
144
145
void jent_gcd_fini(uint64_t *delta_history, size_t nelem)
146
0
{
147
0
  if (delta_history)
148
0
    jent_zfree(delta_history,
149
0
         (unsigned int)(nelem * sizeof(uint64_t)));
150
0
}
151
152
int jent_gcd_get(uint64_t *value)
153
0
{
154
0
  if (!jent_gcd_tested())
155
0
    return 1;
156
157
0
  *value = jent_common_timer_gcd;
158
0
  return 0;
159
0
}
160
161
int jent_gcd_selftest(void)
162
0
{
163
0
#define JENT_GCD_SELFTEST_ELEM 10
164
0
#define JENT_GCD_SELFTEST_EXP 3ULL
165
0
  uint64_t *gcd = jent_gcd_init(JENT_GCD_SELFTEST_ELEM);
166
0
  uint64_t running_gcd, delta_sum;
167
0
  unsigned int i;
168
0
  int ret = EGCD;
169
170
0
  if (!gcd)
171
0
    return EMEM;
172
173
0
  for (i = 0; i < JENT_GCD_SELFTEST_ELEM; i++)
174
0
    jent_gcd_add_value(gcd, i * JENT_GCD_SELFTEST_EXP, i);
175
176
0
  if (jent_gcd_analyze_internal(gcd, JENT_GCD_SELFTEST_ELEM,
177
0
              &running_gcd, &delta_sum))
178
0
    goto out;
179
180
0
  if (running_gcd != JENT_GCD_SELFTEST_EXP)
181
0
    goto out;
182
183
0
  ret = 0;
184
185
0
out:
186
0
  jent_gcd_fini(gcd, JENT_GCD_SELFTEST_ELEM);
187
0
  return ret;
188
0
}