1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define TLOG_TAG "hwrng_unittest"
18 
19 #include <math.h>
20 #include <stddef.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include <lib/rng/trusty_rng.h>
26 #include <trusty_unittest.h>
27 #include <uapi/err.h>
28 
29 static uint32_t _hist[256];
30 static uint8_t _rng_buf[16384];
31 
hwrng_update_hist(uint8_t * data,unsigned int cnt)32 static void hwrng_update_hist(uint8_t* data, unsigned int cnt) {
33     for (unsigned int i = 0; i < cnt; i++) {
34         _hist[data[i]]++;
35     }
36 }
37 
hwrng_show_data(const void * ptr,size_t len)38 static void hwrng_show_data(const void* ptr, size_t len) {
39     uintptr_t address = (uintptr_t)ptr;
40     size_t count;
41     size_t i;
42     fprintf(stderr, "Dumping first hwrng request:\n");
43     for (count = 0; count < len; count += 16) {
44         for (i = 0; i < MIN(len - count, 16); i++) {
45             fprintf(stderr, "0x%02hhx ", *(const uint8_t*)(address + i));
46         }
47         fprintf(stderr, "\n");
48         address += 16;
49     }
50 }
51 
TEST(hwrng,show_data_test)52 TEST(hwrng, show_data_test) {
53     int rc;
54     rc = trusty_rng_hw_rand(_rng_buf, 32);
55     EXPECT_EQ(NO_ERROR, rc, "hwrng test");
56     if (rc == NO_ERROR) {
57         hwrng_show_data(_rng_buf, 32);
58     }
59 }
60 
TEST(hwrng,large_buffer)61 TEST(hwrng, large_buffer) {
62     int rc;
63     rc = trusty_rng_hw_rand(_rng_buf, sizeof(_rng_buf));
64     EXPECT_EQ(NO_ERROR, rc, "hwrng test");
65 }
66 
TEST(hwrng,var_rng_req_test)67 TEST(hwrng, var_rng_req_test) {
68     int rc;
69     unsigned int i;
70     size_t req_cnt;
71     /* Issue 100 hwrng requests of variable sizes */
72     for (i = 0; i < 100; i++) {
73         req_cnt = ((size_t)rand() % MIN(1024, sizeof(_rng_buf))) + 1;
74         rc = trusty_rng_hw_rand(_rng_buf, req_cnt);
75         EXPECT_EQ(NO_ERROR, rc, "hwrng test");
76         if (rc != NO_ERROR) {
77             TLOGI("trusty_rng_hw_rand returned %d\n", rc);
78             continue;
79         }
80     }
81 }
82 
83 #if !WITHOUT_HWRNG_FORWARD_TEST
84 /*
85  * This Test is NOT intended as a replacement for a proper NIST SP 800-22
86  * certification suites. It only attempts to detect detect gross misbehaviors of
87  * rng early on, at low computational costs while not being flaky. It is
88  * adapted from Section 2.13
89  */
90 
91 #define NB_RNG_QUERIES 1000
92 #define LEN_BYTES_RNG_QUERIES 1024
93 #define CUM_SUM_ERR_MSG_SZ 256
94 /*
95  * Standard Normal Cumulative Probability Distribution Function as defined in
96  * Section 5.5.3
97  */
phi(double z)98 static double phi(double z) {
99     return 0.5 * (1.0 + erf(z / sqrt(2.0)));
100 }
101 
102 /*
103  * This is the actual NIST Test as defined in NIST 800-22 Section 2.13. It
104  * will return NO_ERROR if and only if it succeeds, otherwise an error code.
105  */
cumulative_sums_forward_test_helper(void)106 static int cumulative_sums_forward_test_helper(void) {
107     /* n is as defined in NIST 800-22 Section 2.13.7 recommends >=100 */
108     const int64_t n = (NB_RNG_QUERIES * LEN_BYTES_RNG_QUERIES * 8);
109     _Static_assert(
110             n >= 100,
111             "n as defined in NIST 800-22 Section 2.13.7 recommends >=100");
112 
113     int rc;
114     int S = 0;
115     size_t i, j, k;
116 
117     /* largest absolute value of the partial sums */
118     int64_t z = 0;
119 
120     for (i = 0; i < NB_RNG_QUERIES; i++) {
121         rc = trusty_rng_hw_rand(_rng_buf, LEN_BYTES_RNG_QUERIES);
122         if (rc != NO_ERROR) {
123             TLOGE("trusty_rng_hw_rand returned %d", rc);
124             return rc;
125         }
126         for (j = 0; j < LEN_BYTES_RNG_QUERIES; j++) {
127             for (k = 0; k < 8; k++) {
128                 S += (_rng_buf[j] >> k) & 1 ? 1 : -1;
129                 z = MAX(abs(S), z);
130             }
131         }
132     }
133 
134     int64_t start = ((-(double)n / (double)z) + 1.0) / 4.0;
135     int64_t end = (((double)n / (double)z) + 1.0) / 4.0;
136     int64_t start2 = ((-(double)n / (double)z) - 3.0) / 4.0;
137     double p = 1.0;
138     for (int64_t k = start; k <= end; k++) {
139         p -= phi(((4.0 * k + 1.0) * z) / sqrt(n));
140         p += phi(((4.0 * k - 1.0) * z) / sqrt(n));
141     }
142     for (int64_t k = start2; k <= end; k++) {
143         p += phi(((4.0 * k + 3.0) * z) / sqrt(n));
144         p -= phi(((4.0 * k + 1.0) * z) / sqrt(n));
145     }
146 
147     if (p <= 0.01) {
148         trusty_unittest_printf(
149                 "[   WARN   ] NIST 800-22 - Section 2.13.5 Decision Rule (at the 1 Percent Level)\n");
150         return ERR_GENERIC;
151     }
152 
153     return NO_ERROR;
154 }
155 
156 /*
157  * To avoid flakiness on real devices the actual NIST test is attempted 3 times.
158  * The helper which implements the Test as-is has ~1% failure rate.
159  */
TEST(hwrng,cumulative_sums_forward_test)160 TEST(hwrng, cumulative_sums_forward_test) {
161     int ret = NO_ERROR;
162     int counter = 3;
163     do {
164         ret = cumulative_sums_forward_test_helper();
165     } while (ret && counter--);
166 
167     EXPECT_EQ(
168             ret, NO_ERROR,
169             "NIST 800-22 - Section 2.13.5 criteria not met after 3 attempts.");
170 }
171 
172 #endif
173 
TEST(hwrng,stats_test)174 TEST(hwrng, stats_test) {
175     int rc;
176     unsigned int i;
177     size_t req_cnt;
178     uint32_t exp_cnt;
179     uint32_t cnt = 0;
180     uint32_t ave = 0;
181     uint32_t dev = 0;
182     /* issue 100x256 bytes requests */
183     req_cnt = 256;
184     exp_cnt = 1000 * req_cnt;
185     memset(_hist, 0, sizeof(_hist));
186     for (i = 0; i < 1000; i++) {
187         rc = trusty_rng_hw_rand(_rng_buf, req_cnt);
188         EXPECT_EQ(NO_ERROR, rc, "hwrng test");
189         if (rc != NO_ERROR) {
190             TLOGI("trusty_rng_hw_rand returned %d\n", rc);
191             continue;
192         }
193         hwrng_update_hist(_rng_buf, req_cnt);
194     }
195 
196     /* check hwrng stats */
197     for (i = 0; i < 256; i++)
198         cnt += _hist[i];
199     ave = cnt / 256;
200     EXPECT_EQ(exp_cnt, cnt, "hwrng ttl sample cnt");
201     EXPECT_EQ(1000, ave, "hwrng eve sample cnt");
202 
203     /*
204      * Ideally data should be uniformly distributed
205      * Calculate average deviation from ideal model
206      */
207     for (i = 0; i < 256; i++) {
208         uint32_t val = (_hist[i] > ave) ? _hist[i] - ave : ave - _hist[i];
209         dev += val;
210     }
211     dev /= 256;
212     /*
213      * Check if average deviation is within 5% of ideal model
214      * which is fairly arbitrary requirement. It could be useful
215      * to alert is something terribly wrong with rng source.
216      */
217     EXPECT_GT(50, dev, "average dev");
218 }
219 
220 PORT_TEST(hwrng, "com.android.trusty.hwrng.test")
221