Software APIs
status_unittest.cc
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/crypto/impl/status.h"
6 
7 #include <array>
8 
9 #include "gmock/gmock.h"
10 #include "gtest/gtest.h"
11 
12 // NOTE: This test does not verify hardening measures; it only checks that the
13 // "normal" contract of the functions is upheld.
14 
15 namespace status_unittest {
16 namespace {
17 
18 TEST(Status, OkIsHardenedTrue) {
19  EXPECT_EQ(kOtcryptoStatusValueOk, kHardenedBoolTrue);
20 }
21 
22 int HammingDistance(int32_t a, int32_t b) {
23  // The hamming distance is the number of bits different between the two words.
24  return bitfield_popcount32(a ^ b);
25 }
26 
27 // Check the Hamming distances of the top-level error codes.
28 constexpr int kMinimumHammingDistance = 5;
29 TEST(Status, TopLevelStatusHammingDistance) {
30  std::array<otcrypto_status_t, 5> error_codes = {
31  OTCRYPTO_BAD_ARGS, OTCRYPTO_RECOV_ERR, OTCRYPTO_FATAL_ERR,
32  OTCRYPTO_ASYNC_INCOMPLETE, OTCRYPTO_NOT_IMPLEMENTED};
33 
34  // Expect the "OK" code to have a significant Hamming distance from 0.
35  EXPECT_GE(HammingDistance(kOtcryptoStatusValueOk, 0), kMinimumHammingDistance)
36  << "The 'OK' status code " << kOtcryptoStatusValueOk
37  << " is too close to zero.";
38 
39  for (const otcrypto_status_t status1 : error_codes) {
40  // Expect a significant Hamming distance from 0.
41  EXPECT_GE(HammingDistance(status1.value, 0), kMinimumHammingDistance)
42  << "Error code " << status1.value << " is too close to zero.";
43  // Expect an extra significant Hamming distance from the "OK" code.
44  EXPECT_GE(HammingDistance(status1.value, kOtcryptoStatusValueOk),
45  kMinimumHammingDistance)
46  << "Error code " << status1.value << " is too close to the 'OK' value ("
47  << kOtcryptoStatusValueOk << ").";
48 
49  // Expect a significant Hamming distance from all other error codes.
50  for (const otcrypto_status_t status2 : error_codes) {
51  if (status1.value != status2.value) {
52  EXPECT_GE(HammingDistance(status1.value, status2.value),
53  kMinimumHammingDistance)
54  << "Error codes " << status1.value << " and " << status2.value
55  << " are too close to each other.";
56  }
57  }
58  }
59 }
60 
61 TEST(Status, OkIsNonHardenedOk) { EXPECT_EQ(status_ok(OTCRYPTO_OK), true); }
62 
63 TEST(Status, ErrorMacrosNotOk) {
64  // Error macros should evaluate to non-OK statuses.
65  EXPECT_EQ(status_ok(OTCRYPTO_BAD_ARGS), false);
66  EXPECT_EQ(status_ok(OTCRYPTO_RECOV_ERR), false);
67  EXPECT_EQ(status_ok(OTCRYPTO_FATAL_ERR), false);
68  EXPECT_EQ(status_ok(OTCRYPTO_ASYNC_INCOMPLETE), false);
69  EXPECT_EQ(status_ok(OTCRYPTO_NOT_IMPLEMENTED), false);
70 }
71 
72 TEST(Status, ErrorMacrosHaveExpectedValues) {
73  // Error macros should evaluate to specific Abseil error codes.
74  EXPECT_EQ(status_err(OTCRYPTO_BAD_ARGS), kInvalidArgument);
75  EXPECT_EQ(status_err(OTCRYPTO_RECOV_ERR), kAborted);
76  EXPECT_EQ(status_err(OTCRYPTO_FATAL_ERR), kFailedPrecondition);
77  EXPECT_EQ(status_err(OTCRYPTO_ASYNC_INCOMPLETE), kUnavailable);
78  EXPECT_EQ(status_err(OTCRYPTO_NOT_IMPLEMENTED), kUnimplemented);
79 }
80 
81 __attribute__((noinline)) status_t do_hardened_try(status_t status) {
82  HARDENED_TRY(status);
83  return OTCRYPTO_OK;
84 }
85 
86 TEST(Status, HardenedTryOfNonHardenedOkIsError) {
87  EXPECT_EQ(status_err(do_hardened_try(OK_STATUS())), kFailedPrecondition);
88 }
89 
90 TEST(Status, HardenedTryOfHardenedOkIsOk) {
91  EXPECT_EQ(status_ok(do_hardened_try(OTCRYPTO_OK)), true);
92 }
93 
94 TEST(Status, HardenedTryOfErrorIsError) {
95  EXPECT_EQ(status_ok(do_hardened_try(INVALID_ARGUMENT())), false);
96 }
97 
98 TEST(Status, HardenedTryOfErrorWithTruthyArgIsError) {
99  EXPECT_EQ(status_ok(do_hardened_try(INVALID_ARGUMENT(kHardenedBoolTrue))),
100  false);
101 }
102 
103 __attribute__((noinline)) otcrypto_status_t try_interpret(status_t status) {
104  HARDENED_TRY(status);
105  return OTCRYPTO_OK;
106 }
107 
108 TEST(Status, TryInterpretOk) {
109  // Hardened OK should result in an OK status.
110  EXPECT_EQ(status_ok(try_interpret(OTCRYPTO_OK)), true);
111 }
112 
113 TEST(Status, TryInterpretNonHardenedOk) {
114  // Non-hardened OK should result in an error.
115  EXPECT_EQ(status_ok(try_interpret(OK_STATUS())), false);
116 }
117 
118 TEST(Status, TryInterpretErrors) {
119  // Error macros should result in error statuses.
120  EXPECT_EQ(status_ok(try_interpret(OTCRYPTO_BAD_ARGS)), false);
121  EXPECT_EQ(status_ok(try_interpret(OTCRYPTO_RECOV_ERR)), false);
122  EXPECT_EQ(status_ok(try_interpret(OTCRYPTO_FATAL_ERR)), false);
123  EXPECT_EQ(status_ok(try_interpret(OTCRYPTO_ASYNC_INCOMPLETE)), false);
124 }
125 
126 } // namespace
127 } // namespace status_unittest