/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 | } |