Software APIs
verify_test.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/silicon_creator/lib/sigverify/sphincsplus/verify.h"
6 
7 #include <stdint.h>
8 
10 #include "sw/device/lib/base/status.h"
13 #include "sw/device/lib/testing/profile.h"
14 #include "sw/device/lib/testing/test_framework/check.h"
16 
17 // The autogen rule that creates this header creates it in a directory named
18 // after the rule, then manipulates the include path in the
19 // cc_compilation_context to include that directory, so the compiler will find
20 // the version of this file matching the Bazel rule under test.
21 #include "sphincsplus_testvectors.h"
22 
23 // Index of the test vector currently under test
24 static uint32_t test_index = 0;
25 
26 OTTF_DEFINE_TEST_CONFIG();
27 
28 enum {
29  /**
30  * Number of negative tests to run (manipulating the message and checking
31  * that the signature fails).
32  */
33  kNumNegativeTests = 1,
34 };
35 
36 /**
37  * Run the SPHINCS+ verification procedure on the current test.
38  *
39  * @param test Test vector to run.
40  * @param[out] root Output buffer for root node computed from signature.
41  * @param[out] pub_root Output buffer for root node computed from public key.
42  */
44 static rom_error_t run_verify(const spx_verify_test_vector_t *test,
45  uint32_t *root, uint32_t *pub_root) {
46  // Calculate the public-key root to compare against.
47  spx_public_key_root(test->pk, pub_root);
48 
49  // Run verification and print the cycle count.
50  uint64_t t_start = profile_start();
51  rom_error_t err = spx_verify(test->sig, NULL, 0, NULL, 0, NULL, 0, test->msg,
52  test->msg_len, test->pk, root);
53  uint32_t cycles = profile_end(t_start);
54  LOG_INFO("Verification took %u cycles.", cycles);
55 
56  return err;
57 }
58 
59 /**
60  * Run the current test.
61  *
62  * The verification is expected to succeed.
63  */
65 static rom_error_t spx_verify_test(void) {
66  spx_verify_test_vector_t test = spx_verify_tests[test_index];
67 
68  uint32_t root[kSpxVerifyRootNumWords];
69  uint32_t pub_root[kSpxVerifyRootNumWords];
70  RETURN_IF_ERROR(run_verify(&test, root, pub_root));
71 
72  // Ensure that both roots are the same (verification passed).
73  CHECK_ARRAYS_EQ(root, pub_root, kSpxVerifyRootNumWords);
74  return kErrorOk;
75 }
76 
77 /**
78  * Run the current test with a modified message or signature.
79  *
80  * The verification is expected to fail.
81  */
83 static rom_error_t spx_verify_negative_test(void) {
84  spx_verify_test_vector_t test = spx_verify_tests[test_index];
85 
86  if (test.msg_len > 0) {
87  // Bitwise-invert the first byte of the message.
88  test.msg[0] = ~test.msg[0];
89  } else {
90  // If the message is empty, change the signature.
91  test.sig[0] = ~test.sig[0];
92  }
93 
94  uint32_t root[kSpxVerifyRootNumWords];
95  uint32_t pub_root[kSpxVerifyRootNumWords];
96  RETURN_IF_ERROR(run_verify(&test, root, pub_root));
97 
98  // Ensure that the roots are the different (verification failed).
99  CHECK_ARRAYS_NE(root, pub_root, kSpxVerifyRootNumWords);
100  return kErrorOk;
101 }
102 
103 bool test_main(void) {
104  status_t result = OK_STATUS();
105 
106  CHECK(kNumNegativeTests <= kSpxVerifyNumTests,
107  "kNumNegativeTests (%d) cannot be larger than the total number of "
108  "tests (%d).",
109  kNumNegativeTests, kSpxVerifyNumTests);
110 
111  LOG_INFO("Running %d tests with valid signatures.", kSpxVerifyNumTests);
112 
113  for (size_t i = 0; i < kSpxVerifyNumTests; i++) {
114  EXECUTE_TEST(result, spx_verify_test);
115  test_index++;
116  LOG_INFO("Finished test %d of %d.", test_index, kSpxVerifyNumTests);
117  }
118 
119  LOG_INFO("Running %d tests with invalid signatures.", kNumNegativeTests);
120 
121  test_index = 0;
122  for (size_t i = 0; i < kNumNegativeTests; i++) {
123  EXECUTE_TEST(result, spx_verify_negative_test);
124  test_index++;
125  LOG_INFO("Finished negative test %d of %d.", test_index, kNumNegativeTests);
126  }
127 
128  return status_ok(result);
129 }