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

randombit / botan / 6214924625

17 Sep 2023 05:27PM UTC coverage: 91.726% (+0.003%) from 91.723%
6214924625

push

github

web-flow
Merge pull request #3701 from Rohde-Schwarz/fix/xof_without_start

FIX: cSHAKE should not depend on the user calling start()

79217 of 86363 relevant lines covered (91.73%)

8486094.1 hits per line

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

94.39
/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/xof.h>
12
   #include <botan/internal/fmt.h>
13
   #include <botan/internal/stl_util.h>
14

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

22
namespace Botan_Tests {
23

24
#if defined(BOTAN_HAS_XOF)
25

26
class XOF_Tests final : public Text_Based_Test {
×
27
   public:
28
      XOF_Tests() : Text_Based_Test("xof", "In,Out", "Salt,Key,Name") {}
2✔
29

30
      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override {
185✔
31
         const std::vector<uint8_t> in = vars.get_req_bin("In");
185✔
32
         const std::vector<uint8_t> expected = vars.get_req_bin("Out");
185✔
33
         const std::vector<uint8_t> salt = vars.get_opt_bin("Salt");
185✔
34
         const std::vector<uint8_t> key = vars.get_opt_bin("Key");
185✔
35

36
         // used exclusively for cSHAKE
37
         [[maybe_unused]] const std::vector<uint8_t> name = vars.get_opt_bin("Name");
185✔
38

39
         Test::Result result(algo);
370✔
40

41
         const auto providers = [&]() -> std::vector<std::string> {
370✔
42
   #if defined(BOTAN_HAS_CSHAKE_XOF)
43
            if(algo == "cSHAKE-128" || algo == "cSHAKE-256") {
185✔
44
               return {"base"};
16✔
45
            }
46
   #endif
47

48
            return provider_filter(Botan::XOF::providers(algo));
177✔
49
         }();
185✔
50

51
         if(providers.empty()) {
185✔
52
            result.note_missing("XOF " + algo);
×
53
            return result;
×
54
         }
55

56
         for(const auto& provider_ask : providers) {
370✔
57
            auto xof = [&]() -> std::unique_ptr<Botan::XOF> {
370✔
58
   #if defined(BOTAN_HAS_CSHAKE_XOF)
59
               if(algo == "cSHAKE-128") {
362✔
60
                  return std::make_unique<Botan::cSHAKE_128_XOF>(name);
4✔
61
               }
62
               if(algo == "cSHAKE-256") {
181✔
63
                  return std::make_unique<Botan::cSHAKE_256_XOF>(name);
4✔
64
               }
65
   #endif
66
               return Botan::XOF::create(algo, provider_ask);
177✔
67
            }();
185✔
68

69
            if(!xof) {
185✔
70
               result.test_failure(Botan::fmt("XOF {} supported by {} but not found", algo, provider_ask));
×
71
               continue;
×
72
            }
73

74
            const std::string provider(xof->provider());
185✔
75
            result.test_is_nonempty("provider", provider);
185✔
76
            result.test_eq(provider, xof->name(), algo);
185✔
77

78
            // Some XOFs don't accept input at all. We assume that this stays the same
79
            // after calling `XOF::clear()`.
80
            const auto new_accepts_input = xof->accepts_input();
185✔
81

82
            result.confirm("advertised block size is > 0", xof->block_size() > 0);
370✔
83
            result.test_eq("new object may accept input", xof->accepts_input(), new_accepts_input);
185✔
84

85
            // input and output in bulk
86
            xof->start(salt, key);
185✔
87
            xof->update(in);
185✔
88
            result.test_eq("object may accept input before first output", xof->accepts_input(), new_accepts_input);
185✔
89
            result.test_eq("generated output", xof->output_stdvec(expected.size()), expected);
370✔
90
            result.confirm("object does not accept input after first output", !xof->accepts_input());
370✔
91

92
            // if not necessary, invoking start() should be optional
93
            if(salt.empty() && key.empty()) {
185✔
94
               xof->clear();
179✔
95
               xof->update(in);
179✔
96
               result.test_eq("generated output (w/o start())", xof->output_stdvec(expected.size()), expected);
537✔
97
            }
98

99
            // input again and output bytewise
100
            xof->clear();
185✔
101
            result.test_eq("object might accept input after clear()", xof->accepts_input(), new_accepts_input);
185✔
102
            xof->start(salt, key);
185✔
103
            xof->update(in);
185✔
104

105
            std::vector<uint8_t> singlebyte_out(expected.size());
185✔
106
            for(uint8_t& chr : singlebyte_out) {
4,523✔
107
               chr = xof->output_next_byte();
4,338✔
108
            }
109
            result.test_eq("generated singlebyte output", singlebyte_out, expected);
185✔
110

111
            // input and output blocksize-ish wise
112
            auto process_as_blocks = [&](const std::string& id, size_t block_size) {
740✔
113
               auto new_xof = xof->new_object();
555✔
114
               result.test_eq(Botan::fmt("reconstructed XOF may accept input ({})", id),
555✔
115
                              new_xof->accepts_input(),
555✔
116
                              new_accepts_input);
555✔
117

118
               new_xof->start(salt, key);
555✔
119
               std::span<const uint8_t> in_span(in);
555✔
120
               while(!in_span.empty()) {
1,116✔
121
                  const auto bytes = std::min(block_size, in_span.size());
561✔
122
                  new_xof->update(in_span.first(bytes));
561✔
123
                  in_span = in_span.last(in_span.size() - bytes);
561✔
124
               }
125
               std::vector<uint8_t> blockwise_out(expected.size());
555✔
126
               std::span<uint8_t> out_span(blockwise_out);
555✔
127
               while(!out_span.empty()) {
1,110✔
128
                  const auto bytes = std::min(block_size, out_span.size());
555✔
129
                  new_xof->output(out_span.first(bytes));
555✔
130
                  out_span = out_span.last(out_span.size() - bytes);
555✔
131
               }
132
               result.test_eq(Botan::fmt("generated blockwise output ({})", id), blockwise_out, expected);
1,110✔
133
            };
1,110✔
134

135
            process_as_blocks("-1", xof->block_size() - 1);
185✔
136
            process_as_blocks("+0", xof->block_size());
185✔
137
            process_as_blocks("+1", xof->block_size() + 1);
185✔
138

139
            // copy state during processing
140
            try {
185✔
141
               xof->clear();
185✔
142
               xof->start(salt, key);
185✔
143
               xof->update(std::span(in).first(in.size() / 2));
185✔
144
               auto xof2 = xof->copy_state();
185✔
145
               result.test_eq("copied object might still accept input", xof2->accepts_input(), new_accepts_input);
185✔
146
               xof->update(std::span(in).last(in.size() - in.size() / 2));
185✔
147
               xof2->update(std::span(in).last(in.size() - in.size() / 2));
185✔
148
               auto cp_out1 = xof->output_stdvec(expected.size());
185✔
149
               auto cp_out2_1 = xof2->output_stdvec(expected.size() / 2);
185✔
150
               auto xof3 = xof2->copy_state();
185✔
151
               result.confirm("copied object doesn't allow input after reading output", !xof3->accepts_input());
370✔
152
               auto cp_out2_2a = xof2->output_stdvec(expected.size() - expected.size() / 2);
185✔
153
               auto cp_out2_2b = xof3->output_stdvec(expected.size() - expected.size() / 2);
185✔
154
               result.test_eq("output is equal, after state copy", cp_out1, expected);
185✔
155
               result.test_eq("output is equal, after state copy (A)", Botan::concat(cp_out2_1, cp_out2_2a), expected);
370✔
156
               result.test_eq("output is equal, after state copy (B)", Botan::concat(cp_out2_1, cp_out2_2b), expected);
555✔
157
            } catch(const Botan::Not_Implemented&) {
1,110✔
158
               // pass...
159
            }
×
160
         }
370✔
161

162
         return result;
163
      }
565✔
164

165
      std::vector<Test::Result> run_final_tests() override {
1✔
166
         return {
1✔
167
   #if defined(BOTAN_HAS_CSHAKE_XOF)
168
            Botan_Tests::CHECK("cSHAKE without a name", [](Test::Result& result) {
1✔
169
               std::vector<std::unique_ptr<Botan::XOF>> cshakes;
1✔
170
               cshakes.push_back(std::make_unique<Botan::cSHAKE_128_XOF>(""));
1✔
171
               cshakes.push_back(std::make_unique<Botan::cSHAKE_256_XOF>(""));
1✔
172

173
               for(auto& cshake : cshakes) {
3✔
174
                  result.confirm("cSHAKE without a name rejects empty salt", !cshake->valid_salt_length(0));
4✔
175
                  result.confirm("cSHAKE without a name requests at least one byte of salt",
4✔
176
                                 cshake->valid_salt_length(1));
2✔
177
                  result.test_throws("cSHAKE without a name throws without salt", [&]() { cshake->start({}); });
8✔
178
               }
179
            }),
1✔
180
   #endif
181
         };
2✔
182
      }
183
};
184

185
BOTAN_REGISTER_SERIALIZED_TEST("xof", "extendable_output_functions", XOF_Tests);
186

187
#endif
188

189
}  // 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

© 2025 Coveralls, Inc