Software APIs
randomness_quality.c
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
5 #include "sw/device/lib/testing/randomness_quality.h"
6 
7 #include "sw/device/lib/base/status.h"
8 #include "sw/device/lib/testing/test_framework/check.h"
9 
10 enum {
11  /**
12  * Thresholds for the monobit test (alpha=0.05), 5% false negative rate.
13  *
14  * Based on the Handbook of Applied Cryptography, Table 5.2 and section
15  * 5.4.4. To avoid floating-point arithmetic we represent the threshold value
16  * as a fraction.
17  */
18  kMonobitFivePercentThresholdNumerator = 38415,
19  kMonobitFivePercentThresholdDenominator = 10000,
20  /**
21  * Thresholds for the monobit test (alpha=0.01), 1% false negative rate.
22  *
23  * Based on the Handbook of Applied Cryptography, Table 5.2 and section
24  * 5.4.4. To avoid floating-point arithmetic we represent the threshold value
25  * as a fraction.
26  */
27  kMonobitOnePercentThresholdNumerator = 66349,
28  kMonobitOnePercentThresholdDenominator = 10000,
29 };
30 
31 /**
32  * Get the square of the difference of two numbers.
33  *
34  * @param a First operand.
35  * @param b Second operand.
36  * @return Result, (a - b)^2
37  */
38 static uint64_t diff_squared(uint64_t a, uint64_t b) {
39  if (a <= b) {
40  return (b - a) * (b - a);
41  }
42  return (a - b) * (a - b);
43 }
44 
45 status_t randomness_quality_monobit_test(
46  uint8_t *data, size_t len, randomness_quality_significance_t significance) {
47  // Guard against overflow in the bit-count.
48  TRY_CHECK(len <= UINT32_MAX);
49 
50  // Count the number of zeroes and the number of ones in the data.
51  uint32_t num_ones = 0;
52  uint32_t num_zeroes = 0;
53  for (size_t i = 0; i < len; i++) {
54  uint8_t byte = data[i];
55  for (size_t j = 0; j < 8; j++) {
56  num_ones += byte & 1;
57  num_zeroes += (byte ^ 1) & 1;
58  byte >>= 1;
59  }
60  }
61 
62  // Numerical result of the test is (#zeroes - #ones)^2 / bitlen.
63  uint64_t numerator = diff_squared(num_ones, num_zeroes);
64  uint64_t denominator = len * 8;
65 
66  // Retrieve the threshold values.
67  uint64_t threshold_numerator = 0;
68  uint64_t threshold_denominator = 1;
69  switch (significance) {
70  case kRandomnessQualitySignificanceFivePercent: {
71  threshold_numerator = kMonobitFivePercentThresholdNumerator;
72  threshold_denominator = kMonobitFivePercentThresholdDenominator;
73  break;
74  }
75  case kRandomnessQualitySignificanceOnePercent: {
76  threshold_numerator = kMonobitOnePercentThresholdNumerator;
77  threshold_denominator = kMonobitOnePercentThresholdDenominator;
78  break;
79  }
80  default:
81  return INVALID_ARGUMENT();
82  }
83 
84  // Return true if the result value is <= to the threshold.
85  uint64_t lhs = (numerator * threshold_denominator);
86  uint64_t rhs = (threshold_numerator * denominator);
87  TRY_CHECK(lhs <= rhs);
88 
89  return OK_STATUS();
90 }