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

randombit / botan / 6361060095

30 Sep 2023 07:00AM UTC coverage: 91.763% (+0.02%) from 91.747%
6361060095

push

github

web-flow
Merge pull request #3672 from Rohde-Schwarz/feature/xof_in_crystals

79501 of 86637 relevant lines covered (91.76%)

8485723.72 hits per line

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

95.8
/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

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

28
namespace Botan_Tests {
29

30
#if defined(BOTAN_HAS_XOF)
31

32
class XOF_Tests final : public Text_Based_Test {
×
33
   public:
34
      XOF_Tests() : Text_Based_Test("xof", "In,Out", "Salt,Key,Name") {}
2✔
35

36
      Test::Result run_one_test(const std::string& algo, const VarMap& vars) override {
212✔
37
         const std::vector<uint8_t> in = vars.get_req_bin("In");
212✔
38
         const std::vector<uint8_t> expected = vars.get_req_bin("Out");
212✔
39
         const std::vector<uint8_t> salt = vars.get_opt_bin("Salt");
212✔
40
         const std::vector<uint8_t> key = vars.get_opt_bin("Key");
212✔
41

42
         // used exclusively for cSHAKE
43
         [[maybe_unused]] const std::vector<uint8_t> name = vars.get_opt_bin("Name");
212✔
44

45
         Test::Result result(algo);
424✔
46

47
         const auto providers = [&]() -> std::vector<std::string> {
424✔
48
   #if defined(BOTAN_HAS_CSHAKE_XOF)
49
            if(algo == "cSHAKE-128" || algo == "cSHAKE-256") {
212✔
50
               return {"base"};
16✔
51
            }
52
   #endif
53

54
   #if defined(BOTAN_HAS_AES_CRYSTALS_XOF)
55
            if(algo == "CTR-BE(AES-256)") {
204✔
56
               return {"base"};
54✔
57
            }
58
   #endif
59
            return provider_filter(Botan::XOF::providers(algo));
177✔
60
         }();
212✔
61

62
         if(providers.empty()) {
212✔
63
            result.note_missing("XOF " + algo);
×
64
            return result;
×
65
         }
66

67
         for(const auto& provider_ask : providers) {
424✔
68
            auto xof = [&]() -> std::unique_ptr<Botan::XOF> {
424✔
69
   #if defined(BOTAN_HAS_CSHAKE_XOF)
70
               if(algo == "cSHAKE-128") {
389✔
71
                  return std::make_unique<Botan::cSHAKE_128_XOF>(name);
4✔
72
               }
73
               if(algo == "cSHAKE-256") {
208✔
74
                  return std::make_unique<Botan::cSHAKE_256_XOF>(name);
4✔
75
               }
76
   #endif
77

78
   #if defined(BOTAN_HAS_AES_CRYSTALS_XOF)
79
               if(algo == "CTR-BE(AES-256)") {
204✔
80
                  return std::make_unique<Botan::AES_256_CTR_XOF>();
27✔
81
               }
82
   #endif
83
               return Botan::XOF::create(algo, provider_ask);
177✔
84
            }();
212✔
85

86
            if(!xof) {
212✔
87
               result.test_failure(Botan::fmt("XOF {} supported by {} but not found", algo, provider_ask));
×
88
               continue;
×
89
            }
90

91
            const std::string provider(xof->provider());
212✔
92
            result.test_is_nonempty("provider", provider);
212✔
93
            result.test_eq(provider, xof->name(), algo);
212✔
94

95
            // Some XOFs don't accept input at all. We assume that this stays the same
96
            // after calling `XOF::clear()`.
97
            const auto new_accepts_input = xof->accepts_input();
212✔
98

99
            result.confirm("advertised block size is > 0", xof->block_size() > 0);
424✔
100
            result.test_eq("new object may accept input", xof->accepts_input(), new_accepts_input);
212✔
101

102
            // input and output in bulk
103
            xof->start(salt, key);
212✔
104
            xof->update(in);
212✔
105
            result.test_eq("object may accept input before first output", xof->accepts_input(), new_accepts_input);
212✔
106
            result.test_eq("generated output", xof->output_stdvec(expected.size()), expected);
424✔
107
            result.confirm("object does not accept input after first output", !xof->accepts_input());
424✔
108

109
            // if not necessary, invoking start() should be optional
110
            if(salt.empty() && key.empty()) {
212✔
111
               xof->clear();
179✔
112
               xof->update(in);
179✔
113
               result.test_eq("generated output (w/o start())", xof->output_stdvec(expected.size()), expected);
537✔
114
            }
115

116
            // input again and output bytewise
117
            xof->clear();
212✔
118
            result.test_eq("object might accept input after clear()", xof->accepts_input(), new_accepts_input);
212✔
119
            xof->start(salt, key);
212✔
120
            xof->update(in);
212✔
121

122
            std::vector<uint8_t> singlebyte_out(expected.size());
212✔
123
            for(uint8_t& chr : singlebyte_out) {
5,780✔
124
               chr = xof->output_next_byte();
5,568✔
125
            }
126
            result.test_eq("generated singlebyte output", singlebyte_out, expected);
212✔
127

128
            // input and output blocksize-ish wise
129
            auto process_as_blocks = [&](const std::string& id, size_t block_size) {
848✔
130
               auto new_xof = xof->new_object();
636✔
131
               result.test_eq(Botan::fmt("reconstructed XOF may accept input ({})", id),
1,272✔
132
                              new_xof->accepts_input(),
636✔
133
                              new_accepts_input);
636✔
134

135
               new_xof->start(salt, key);
636✔
136
               std::span<const uint8_t> in_span(in);
636✔
137
               while(!in_span.empty()) {
1,197✔
138
                  const auto bytes = std::min(block_size, in_span.size());
561✔
139
                  new_xof->update(in_span.first(bytes));
561✔
140
                  in_span = in_span.last(in_span.size() - bytes);
561✔
141
               }
142
               std::vector<uint8_t> blockwise_out(expected.size());
636✔
143
               std::span<uint8_t> out_span(blockwise_out);
636✔
144
               while(!out_span.empty()) {
1,459✔
145
                  const auto bytes = std::min(block_size, out_span.size());
823✔
146
                  new_xof->output(out_span.first(bytes));
823✔
147
                  out_span = out_span.last(out_span.size() - bytes);
823✔
148
               }
149
               result.test_eq(Botan::fmt("generated blockwise output ({})", id), blockwise_out, expected);
1,272✔
150
            };
1,272✔
151

152
            process_as_blocks("-1", xof->block_size() - 1);
212✔
153
            process_as_blocks("+0", xof->block_size());
212✔
154
            process_as_blocks("+1", xof->block_size() + 1);
212✔
155

156
            // copy state during processing
157
            try {
212✔
158
               xof->clear();
212✔
159
               xof->start(salt, key);
212✔
160
               xof->update(std::span(in).first(in.size() / 2));
212✔
161
               auto xof2 = xof->copy_state();
212✔
162
               result.test_eq("copied object might still accept input", xof2->accepts_input(), new_accepts_input);
185✔
163
               xof->update(std::span(in).last(in.size() - in.size() / 2));
185✔
164
               xof2->update(std::span(in).last(in.size() - in.size() / 2));
185✔
165
               auto cp_out1 = xof->output_stdvec(expected.size());
185✔
166
               auto cp_out2_1 = xof2->output_stdvec(expected.size() / 2);
185✔
167
               auto xof3 = xof2->copy_state();
185✔
168
               result.confirm("copied object doesn't allow input after reading output", !xof3->accepts_input());
370✔
169
               auto cp_out2_2a = xof2->output_stdvec(expected.size() - expected.size() / 2);
185✔
170
               auto cp_out2_2b = xof3->output_stdvec(expected.size() - expected.size() / 2);
185✔
171
               result.test_eq("output is equal, after state copy", cp_out1, expected);
185✔
172
               result.test_eq("output is equal, after state copy (A)", Botan::concat(cp_out2_1, cp_out2_2a), expected);
370✔
173
               result.test_eq("output is equal, after state copy (B)", Botan::concat(cp_out2_1, cp_out2_2b), expected);
555✔
174
            } catch(const Botan::Not_Implemented&) {
1,137✔
175
               // pass...
176
            }
27✔
177
         }
424✔
178

179
         return result;
180
      }
700✔
181

182
      std::vector<Test::Result> run_final_tests() override {
1✔
183
         return {
1✔
184
   #if defined(BOTAN_HAS_CSHAKE_XOF)
185
            Botan_Tests::CHECK(
186
               "cSHAKE without a name",
187
               [](Test::Result& result) {
1✔
188
                  std::vector<std::unique_ptr<Botan::XOF>> cshakes;
1✔
189
                  cshakes.push_back(std::make_unique<Botan::cSHAKE_128_XOF>(""));
1✔
190
                  cshakes.push_back(std::make_unique<Botan::cSHAKE_256_XOF>(""));
1✔
191

192
                  for(auto& cshake : cshakes) {
3✔
193
                     result.confirm("cSHAKE without a name rejects empty salt", !cshake->valid_salt_length(0));
4✔
194
                     result.confirm("cSHAKE without a name requests at least one byte of salt",
4✔
195
                                    cshake->valid_salt_length(1));
2✔
196
                     result.test_throws("cSHAKE without a name throws without salt", [&]() { cshake->start({}); });
8✔
197
                  }
198
               }),
1✔
199
   #endif
200
   #if defined(BOTAN_HAS_AES_CRYSTALS_XOF)
201
               Botan_Tests::CHECK("AES-256/CTR XOF failure modes", [](Test::Result& result) {
1✔
202
                  Botan::AES_256_CTR_XOF aes_xof;
1✔
203
                  result.test_throws("AES-256/CTR XOF throws for empty key", [&]() { aes_xof.start({}, {}); });
3✔
204
                  result.test_throws("AES-256/CTR XOF throws for too long key",
2✔
205
                                     [&]() { aes_xof.start({}, std::vector<uint8_t>(33)); });
1✔
206
                  result.test_throws("AES-256/CTR XOF throws for too long IV",
2✔
207
                                     [&]() { aes_xof.start(std::vector<uint8_t>(17), std::vector<uint8_t>(32)); });
2✔
208
               }),
1✔
209
   #endif
210
         };
3✔
211
      }
212
};
213

214
BOTAN_REGISTER_SERIALIZED_TEST("xof", "extendable_output_functions", XOF_Tests);
215

216
#endif
217

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