• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 23111636944

15 Mar 2026 01:45PM UTC coverage: 89.732% (+0.002%) from 89.73%
23111636944

Pull #5446

github

web-flow
Merge 75b20e42b into e9952d62f
Pull Request #5446: Address (or silence) various new warnings from clang-tidy 22

104217 of 116142 relevant lines covered (89.73%)

11426748.42 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.31
/src/tests/test_xof.cpp
1
/*
2
* (C) 2023 Jack Lloyd
3
*     2023 Fabian Albert, René Meusel - Rohde & Schwarz Cybersecurity
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7

8
#include "tests.h"
9

10
#if defined(BOTAN_HAS_XOF)
11
   #include <botan/exceptn.h>
12
   #include <botan/rng.h>
13
   #include <botan/xof.h>
14
   #include <botan/internal/concat_util.h>
15
   #include <botan/internal/fmt.h>
16

17
   #if defined(BOTAN_HAS_CSHAKE_XOF)
18
      // This XOF implementation is not exposed via the library's public interface
19
      // and is therefore not registered in the XOF::create() factory.
20
      #include <botan/internal/cshake_xof.h>
21
   #endif
22

23
   #if defined(BOTAN_HAS_AES_CRYSTALS_XOF)
24
      // This XOF implementation is not exposed via the library's public interface
25
      // and is therefore not registered in the XOF::create() factory.
26
      #include <botan/internal/aes_crystals_xof.h>
27
   #endif
28
#endif
29

30
namespace Botan_Tests {
31

32
namespace {
33

34
#if defined(BOTAN_HAS_XOF)
35

36
class XOF_Tests final : public Text_Based_Test {
×
37
   public:
38
      XOF_Tests() : Text_Based_Test("xof", "In,Out", "Salt,Key,Name") {}
2✔
39

40
      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override {
318✔
41
         const std::vector<uint8_t> in = vars.get_req_bin("In");
318✔
42
         const std::vector<uint8_t> expected = vars.get_req_bin("Out");
318✔
43
         const std::vector<uint8_t> salt = vars.get_opt_bin("Salt");
318✔
44
         const std::vector<uint8_t> key = vars.get_opt_bin("Key");
318✔
45

46
         // used exclusively for cSHAKE
47
         [[maybe_unused]] const std::vector<uint8_t> name = vars.get_opt_bin("Name");
318✔
48

49
         Test::Result result(algo);
318✔
50

51
         const auto providers = [&]() -> std::vector<std::string> {
954✔
52
   #if defined(BOTAN_HAS_CSHAKE_XOF)
53
            if(algo == "cSHAKE-128" || algo == "cSHAKE-256") {
318✔
54
               return {"base"};
8✔
55
            }
56
   #endif
57

58
   #if defined(BOTAN_HAS_AES_CRYSTALS_XOF)
59
            if(algo == "CTR-BE(AES-256)") {
310✔
60
               return {"base"};
27✔
61
            }
62
   #endif
63
            return provider_filter(Botan::XOF::providers(algo));
283✔
64
         }();
318✔
65

66
         if(providers.empty()) {
318✔
67
            result.note_missing("XOF " + algo);
×
68
            return result;
×
69
         }
70

71
         for(const auto& provider_ask : providers) {
636✔
72
            auto xof = [&]() -> std::unique_ptr<Botan::XOF> {
954✔
73
   #if defined(BOTAN_HAS_CSHAKE_XOF)
74
               if(algo == "cSHAKE-128") {
318✔
75
                  return std::make_unique<Botan::cSHAKE_128_XOF>(name);
4✔
76
               }
77
               if(algo == "cSHAKE-256") {
314✔
78
                  return std::make_unique<Botan::cSHAKE_256_XOF>(name);
4✔
79
               }
80
   #endif
81

82
   #if defined(BOTAN_HAS_AES_CRYSTALS_XOF)
83
               if(algo == "CTR-BE(AES-256)") {
310✔
84
                  return std::make_unique<Botan::AES_256_CTR_XOF>();
27✔
85
               }
86
   #endif
87
               return Botan::XOF::create(algo, provider_ask);
283✔
88
            }();
318✔
89

90
            if(!xof) {
318✔
91
               result.test_failure(Botan::fmt("XOF {} supported by {} but not found", algo, provider_ask));
×
92
               continue;
×
93
            }
94

95
            const std::string provider(xof->provider());
318✔
96
            result.test_str_not_empty("provider", provider);
318✔
97
            result.test_str_eq(provider, xof->name(), algo);
318✔
98

99
            // Some XOFs don't accept input at all. We assume that this stays the same
100
            // after calling `XOF::clear()`.
101
            const auto new_accepts_input = xof->accepts_input();
318✔
102

103
            result.test_is_true("advertised block size is > 0", xof->block_size() > 0);
318✔
104
            result.test_bool_eq("new object may accept input", xof->accepts_input(), new_accepts_input);
318✔
105

106
            // input and output in bulk
107
            xof->start(salt, key);
318✔
108
            xof->update(in);
318✔
109
            result.test_bool_eq("object may accept input before first output", xof->accepts_input(), new_accepts_input);
318✔
110
            result.test_bin_eq("generated output", xof->output_stdvec(expected.size()), expected);
318✔
111
            result.test_is_true("object does not accept input after first output", !xof->accepts_input());
318✔
112

113
            // input and output (overwriting existing data)
114
            // -> regression test where the buffer content was inadvertently
115
            //    xor'ed into the state. Fine with zero-buffer not so fine with
116
            //    random buffer -.-
117
            xof->clear();
318✔
118
            xof->start(salt, key);
318✔
119
            xof->update(in);
318✔
120
            auto nonzero_data = rng().random_vec(expected.size());
318✔
121
            xof->output(nonzero_data);
318✔
122
            result.test_bin_eq("generated output (overwriting existing data)", nonzero_data, expected);
318✔
123

124
            // if not necessary, invoking start() should be optional
125
            if(salt.empty() && key.empty()) {
318✔
126
               xof->clear();
285✔
127
               xof->update(in);
285✔
128
               result.test_bin_eq("generated output (w/o start())", xof->output_stdvec(expected.size()), expected);
570✔
129
            }
130

131
            // input again and output bytewise
132
            xof->clear();
318✔
133
            result.test_bool_eq("object might accept input after clear()", xof->accepts_input(), new_accepts_input);
318✔
134
            xof->start(salt, key);
318✔
135
            xof->update(in);
318✔
136

137
            std::vector<uint8_t> singlebyte_out(expected.size());
318✔
138
            for(uint8_t& chr : singlebyte_out) {
12,358✔
139
               chr = xof->output_next_byte();
12,040✔
140
            }
141
            result.test_bin_eq("generated singlebyte output", singlebyte_out, expected);
318✔
142

143
            // input and output blocksize-ish wise
144
            auto process_as_blocks = [&](const std::string& id, size_t block_size) {
1,272✔
145
               auto new_xof = xof->new_object();
954✔
146
               result.test_bool_eq(Botan::fmt("reconstructed XOF may accept input ({})", id),
954✔
147
                                   new_xof->accepts_input(),
954✔
148
                                   new_accepts_input);
954✔
149

150
               new_xof->start(salt, key);
954✔
151
               std::span<const uint8_t> in_span(in);
954✔
152
               while(!in_span.empty()) {
3,542✔
153
                  const auto bytes = std::min(block_size, in_span.size());
2,588✔
154
                  new_xof->update(in_span.first(bytes));
2,588✔
155
                  in_span = in_span.last(in_span.size() - bytes);
2,588✔
156
               }
157
               std::vector<uint8_t> blockwise_out(expected.size());
954✔
158
               std::span<uint8_t> out_span(blockwise_out);
954✔
159
               while(!out_span.empty()) {
4,413✔
160
                  const auto bytes = std::min(block_size, out_span.size());
3,459✔
161
                  new_xof->output(out_span.first(bytes));
3,459✔
162
                  out_span = out_span.last(out_span.size() - bytes);
3,459✔
163
               }
164
               result.test_bin_eq(Botan::fmt("generated blockwise output ({})", id), blockwise_out, expected);
1,908✔
165
            };
1,908✔
166

167
            process_as_blocks("-1", xof->block_size() - 1);
318✔
168
            process_as_blocks("+0", xof->block_size());
318✔
169
            process_as_blocks("+1", xof->block_size() + 1);
318✔
170

171
            // copy state during processing
172
            try {
318✔
173
               xof->clear();
318✔
174
               xof->start(salt, key);
318✔
175
               xof->update(std::span(in).first(in.size() / 2));
318✔
176
               auto xof2 = xof->copy_state();
318✔
177
               result.test_bool_eq("copied object might still accept input", xof2->accepts_input(), new_accepts_input);
291✔
178
               xof->update(std::span(in).last(in.size() - in.size() / 2));
291✔
179
               xof2->update(std::span(in).last(in.size() - in.size() / 2));
291✔
180
               auto cp_out1 = xof->output_stdvec(expected.size());
291✔
181
               auto cp_out2_1 = xof2->output_stdvec(expected.size() / 2);
291✔
182
               auto xof3 = xof2->copy_state();
291✔
183
               result.test_is_true("copied object doesn't allow input after reading output", !xof3->accepts_input());
291✔
184
               auto cp_out2_2a = xof2->output_stdvec(expected.size() - expected.size() / 2);
291✔
185
               auto cp_out2_2b = xof3->output_stdvec(expected.size() - expected.size() / 2);
291✔
186
               result.test_bin_eq("output is equal, after state copy", cp_out1, expected);
291✔
187
               result.test_bin_eq(
291✔
188
                  "output is equal, after state copy (A)", Botan::concat(cp_out2_1, cp_out2_2a), expected);
×
189
               result.test_bin_eq(
291✔
190
                  "output is equal, after state copy (B)", Botan::concat(cp_out2_1, cp_out2_2b), expected);
291✔
191
            } catch(const Botan::Not_Implemented&) {
1,772✔
192
               // pass...
193
            }
27✔
194
         }
954✔
195

196
         return result;
197
      }
1,018✔
198

199
      std::vector<Test::Result> run_final_tests() override {
1✔
200
         return {
1✔
201
   #if defined(BOTAN_HAS_CSHAKE_XOF)
202
            CHECK("cSHAKE without a name",
203
                  [](Test::Result& result) {
1✔
204
                     std::vector<std::unique_ptr<Botan::XOF>> cshakes;
1✔
205
                     cshakes.push_back(std::make_unique<Botan::cSHAKE_128_XOF>(""));
1✔
206
                     cshakes.push_back(std::make_unique<Botan::cSHAKE_256_XOF>(""));
1✔
207

208
                     for(auto& cshake : cshakes) {
3✔
209
                        result.test_is_true("cSHAKE without a name rejects empty salt", !cshake->valid_salt_length(0));
2✔
210
                        result.test_is_true("cSHAKE without a name requests at least one byte of salt",
2✔
211
                                            cshake->valid_salt_length(1));
2✔
212
                        result.test_throws("cSHAKE without a name throws without salt", [&]() { cshake->start({}); });
4✔
213
                     }
214
                  }),
1✔
215
   #endif
216
   #if defined(BOTAN_HAS_AES_CRYSTALS_XOF)
217
               CHECK("AES-256/CTR XOF failure modes", [](Test::Result& result) {
1✔
218
                  Botan::AES_256_CTR_XOF aes_xof;
1✔
219
                  result.test_throws("AES-256/CTR XOF throws for empty key", [&]() { aes_xof.start({}, {}); });
2✔
220
                  result.test_throws("AES-256/CTR XOF throws for too long key",
1✔
221
                                     [&]() { aes_xof.start({}, std::vector<uint8_t>(33)); });
2✔
222
                  result.test_throws("AES-256/CTR XOF throws for too long IV",
1✔
223
                                     [&]() { aes_xof.start(std::vector<uint8_t>(17), std::vector<uint8_t>(32)); });
3✔
224
               }),
1✔
225
   #endif
226
         };
3✔
227
      }
1✔
228
};
229

230
BOTAN_REGISTER_SERIALIZED_TEST("xof", "extendable_output_functions", XOF_Tests);
231

232
#endif
233

234
}  // namespace
235

236
}  // namespace Botan_Tests
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc