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

randombit / botan / 23225340130

18 Mar 2026 01:53AM UTC coverage: 89.677% (-0.001%) from 89.678%
23225340130

push

github

web-flow
Merge pull request #5456 from randombit/jack/clang-tidy-22

Fix various warnings from clang-tidy 22

104438 of 116460 relevant lines covered (89.68%)

11819947.55 hits per line

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

98.62
/src/tests/test_filters.cpp
1
/*
2
* (C) 2016 Daniel Neus
3
*     2016,2017 Jack Lloyd
4
*     2017 René Korthaus
5
*     2017 Philippe Lieser
6
*
7
* Botan is released under the Simplified BSD License (see license.txt)
8
*/
9

10
#include "tests.h"
11

12
#if defined(BOTAN_HAS_FILTERS)
13
   #include <botan/data_snk.h>
14
   #include <botan/filters.h>
15
   #include <botan/hex.h>
16
   #include <botan/pipe.h>
17
   #include <botan/internal/secqueue.h>
18
   #include <botan/internal/target_info.h>
19
   #include <sstream>
20
#endif
21

22
#if defined(BOTAN_HAS_PIPE_UNIXFD_IO)
23
   #include <botan/fd_unix.h>
24
   #include <unistd.h>
25
#endif
26

27
#if defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
28
   #include <fstream>
29
#endif
30

31
namespace Botan_Tests {
32

33
namespace {
34

35
#if defined(BOTAN_HAS_FILTERS)
36

37
// NOLINTBEGIN(*-owning-memory)
38

39
class Filter_Tests final : public Test {
1✔
40
   public:
41
      std::vector<Test::Result> run() override {
1✔
42
         std::vector<Test::Result> results;
1✔
43

44
         results.push_back(test_secqueue());
2✔
45
         results.push_back(test_data_src_sink());
2✔
46
         results.push_back(test_data_src_sink_flush());
2✔
47
         results.push_back(test_pipe_io());
2✔
48
         results.push_back(test_pipe_fd_io());
2✔
49
         results.push_back(test_pipe_errors());
2✔
50
         results.push_back(test_pipe_hash());
2✔
51
         results.push_back(test_pipe_mac());
2✔
52
         results.push_back(test_pipe_stream());
2✔
53
         results.push_back(test_pipe_cbc());
2✔
54
         results.push_back(test_pipe_cfb());
2✔
55
         results.push_back(test_pipe_compress());
2✔
56
         results.push_back(test_pipe_compress_bzip2());
2✔
57
         results.push_back(test_pipe_codec());
2✔
58
         results.push_back(test_fork());
2✔
59
         results.push_back(test_chain());
2✔
60
         results.push_back(test_threaded_fork());
2✔
61

62
         return results;
1✔
63
      }
×
64

65
   private:
66
      static Test::Result test_secqueue() {
1✔
67
         Test::Result result("SecureQueue");
1✔
68

69
         try {
1✔
70
            Botan::SecureQueue queue_a;
1✔
71
            result.test_is_false("queue not attachable", queue_a.attachable());
1✔
72

73
            std::vector<uint8_t> test_data = {0x24, 0xB2, 0xBF, 0xC2, 0xE6, 0xD4, 0x7E, 0x04, 0x67, 0xB3};
1✔
74
            queue_a.write(test_data.data(), test_data.size());
1✔
75

76
            result.test_sz_eq("size of SecureQueue is correct", queue_a.size(), test_data.size());
1✔
77
            result.test_sz_eq("0 bytes read so far from SecureQueue", queue_a.get_bytes_read(), 0);
1✔
78

79
            result.test_is_true("check_available", queue_a.check_available(1));
1✔
80
            result.test_is_false("check_available", queue_a.check_available(50));
1✔
81
            uint8_t b = 0;
1✔
82
            const size_t bytes_read = queue_a.read_byte(b);
1✔
83
            result.test_sz_eq("1 byte read", bytes_read, 1);
1✔
84

85
            Botan::secure_vector<uint8_t> produced(b);
1✔
86
            Botan::secure_vector<uint8_t> expected(test_data.at(0));
1✔
87
            result.test_bin_eq("byte read is correct", produced, expected);
1✔
88

89
            result.test_sz_eq("1 bytes read so far from SecureQueue", queue_a.get_bytes_read(), 1);
1✔
90

91
            const Botan::SecureQueue queue_b;
1✔
92
            queue_a = queue_b;
1✔
93
            result.test_sz_eq("bytes_read is set correctly", queue_a.get_bytes_read(), 0);
1✔
94
         } catch(std::exception& e) {
4✔
95
            result.test_failure("SecureQueue", e.what());
×
96
         }
×
97

98
         return result;
1✔
99
      }
×
100

101
      static Test::Result test_data_src_sink() {
1✔
102
         Test::Result result("DataSink");
1✔
103

104
   #if defined(BOTAN_HAS_CODEC_FILTERS)
105
         std::ostringstream oss;
1✔
106

107
         Botan::Pipe pipe(new Botan::Hex_Decoder, new Botan::DataSink_Stream(oss));
1✔
108

109
         Botan::DataSource_Memory input_mem("65666768");
1✔
110
         pipe.process_msg(input_mem);
1✔
111

112
         result.test_str_eq("output string", oss.str(), "efgh");
1✔
113

114
         Botan::DataSource_Memory input_mem2("41414141");
1✔
115
         pipe.process_msg(input_mem2);
1✔
116

117
         result.test_str_eq("output string", oss.str(), "efghAAAA");
1✔
118

119
         std::istringstream iss("4343");
1✔
120
         Botan::DataSource_Stream input_strm(iss);
1✔
121
         pipe.process_msg(input_strm);
1✔
122

123
         result.test_str_eq("output string", oss.str(), "efghAAAACC");
1✔
124
   #endif
125
         return result;
1✔
126
      }
3✔
127

128
      static Test::Result test_data_src_sink_flush() {
1✔
129
         Test::Result result("DataSinkFlush");
1✔
130

131
   #if defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
132

133
         const std::string tmp_name = Test::temp_file_name("botan_test_data_src_sink_flush.tmp");
1✔
134
         if(tmp_name.empty()) {
1✔
135
            result.test_failure("Failed to create temporary file");
×
136
            return result;
137
         }
138

139
         std::ofstream outfile(tmp_name);
1✔
140

141
         Botan::Pipe pipe(new Botan::Hex_Decoder, new Botan::DataSink_Stream(outfile));
1✔
142

143
         Botan::DataSource_Memory input_mem("65666768");
1✔
144
         pipe.process_msg(input_mem);
1✔
145

146
         std::ifstream outfile_read(tmp_name);
1✔
147
         std::stringstream ss;
1✔
148
         ss << outfile_read.rdbuf();
1✔
149

150
         result.test_str_eq("output string", ss.str(), "efgh");
1✔
151

152
         // ensure files are closed
153
         outfile.close();
1✔
154
         outfile_read.close();
1✔
155

156
         if(std::remove(tmp_name.c_str()) != 0) {
1✔
157
            result.test_failure("Failed to remove temporary file at conclusion of test");
×
158
         }
159
   #endif
160
         return result;
1✔
161
      }
3✔
162

163
      static Test::Result test_pipe_io() {
1✔
164
         Test::Result result("Pipe I/O operators");
1✔
165

166
   #if defined(BOTAN_HAS_CODEC_FILTERS)
167
         Botan::Pipe pipe(new Botan::Hex_Encoder);
1✔
168

169
         pipe.process_msg("ABCD");
1✔
170

171
         std::ostringstream oss;
1✔
172
         oss << pipe;
1✔
173
         result.test_str_eq("output string", oss.str(), "41424344");
1✔
174

175
         std::istringstream iss("AAAA");
1✔
176
         pipe.start_msg();
1✔
177
         iss >> pipe;
1✔
178
         pipe.end_msg();
1✔
179

180
         pipe.set_default_msg(1);
1✔
181
         oss << pipe;
1✔
182
         result.test_str_eq("output string2", oss.str(), "4142434441414141");
1✔
183
   #endif
184

185
         return result;
1✔
186
      }
1✔
187

188
      static Test::Result test_pipe_errors() {
1✔
189
         Test::Result result("Pipe");
1✔
190

191
         Botan::Pipe pipe;
1✔
192

193
         pipe.append(nullptr);          // ignored
1✔
194
         pipe.append_filter(nullptr);   // ignored
1✔
195
         pipe.prepend(nullptr);         // ignored
1✔
196
         pipe.prepend_filter(nullptr);  // ignored
1✔
197
         pipe.pop();                    // empty pipe, so ignored
1✔
198

199
         auto queue_filter = std::make_shared<Botan::SecureQueue>();
1✔
200

201
         // can't explicitly insert a queue into the pipe because they are implicit
202
         result.test_throws(
1✔
203
            "pipe error", "Pipe::append: SecureQueue cannot be used", [&]() { pipe.append(queue_filter.get()); });
2✔
204

205
         result.test_throws(
1✔
206
            "pipe error", "Pipe::prepend: SecureQueue cannot be used", [&]() { pipe.prepend(queue_filter.get()); });
2✔
207

208
         pipe.append_filter(new Botan::BitBucket);  // succeeds
1✔
209
         pipe.pop();
1✔
210

211
         pipe.prepend_filter(new Botan::BitBucket);  // succeeds
1✔
212
         pipe.pop();
1✔
213

214
         pipe.start_msg();
1✔
215

216
         auto filter = std::make_unique<Botan::BitBucket>();
1✔
217

218
         // now inside a message, cannot modify pipe structure
219

220
         result.test_throws("pipe error", "Cannot call Pipe::append_filter after start_msg", [&]() {
1✔
221
            pipe.append_filter(filter.get());
1✔
222
         });
223

224
         result.test_throws("pipe error", "Cannot call Pipe::prepend_filter after start_msg", [&]() {
1✔
225
            pipe.prepend_filter(filter.get());
1✔
226
         });
227

228
         result.test_throws(
1✔
229
            "pipe error", "Cannot append to a Pipe while it is processing", [&]() { pipe.append(filter.get()); });
2✔
230

231
         result.test_throws(
1✔
232
            "pipe error", "Cannot prepend to a Pipe while it is processing", [&]() { pipe.prepend(filter.get()); });
2✔
233

234
         result.test_throws("pipe error", "Cannot pop off a Pipe while it is processing", [&]() { pipe.pop(); });
2✔
235

236
         pipe.end_msg();
1✔
237

238
         result.test_throws("pipe error", "Cannot call Pipe::append_filter after start_msg", [&]() {
1✔
239
            pipe.append_filter(filter.get());
1✔
240
         });
241

242
         result.test_throws("pipe error", "Cannot call Pipe::prepend_filter after start_msg", [&]() {
1✔
243
            pipe.prepend_filter(filter.get());
1✔
244
         });
245

246
         result.test_throws("pipe error", "Pipe::read: Invalid message number 100", [&]() {
1✔
247
            uint8_t b = 0;
1✔
248
            [[maybe_unused]] const size_t got = pipe.read(&b, 1, 100);
1✔
249
         });
250

251
         pipe.append(nullptr);   // ignored
1✔
252
         pipe.prepend(nullptr);  // ignored
1✔
253
         pipe.pop();             // empty pipe, so ignored
1✔
254

255
         return result;
2✔
256
      }
2✔
257

258
      static Test::Result test_pipe_mac() {
1✔
259
         Test::Result result("Pipe");
1✔
260

261
   #if defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_HAS_HMAC) && defined(BOTAN_HAS_SHA2_32)
262
         const Botan::SymmetricKey key("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
1✔
263

264
         Botan::Keyed_Filter* mac_filter = new Botan::MAC_Filter("HMAC(SHA-256)", key, 12);
1✔
265

266
         mac_filter->set_iv(Botan::InitializationVector());  // ignored
1✔
267

268
         result.test_throws("Keyed_Filter::set_iv throws if not implemented",
1✔
269
                            "IV length 1 is invalid for HMAC(SHA-256)",
270
                            [mac_filter]() { mac_filter->set_iv(Botan::InitializationVector("AA")); });
2✔
271

272
         Botan::Pipe pipe(mac_filter, new Botan::Base64_Encoder);
1✔
273

274
         pipe.process_msg("Hi");
1✔
275
         pipe.process_msg("Bye");
1✔
276
         pipe.process_msg("Hi");
1✔
277

278
         result.test_str_eq("MAC 1", pipe.read_all_as_string(0), "e7NoVbtudgU0QiCZ");
1✔
279
         result.test_str_eq("MAC 2", pipe.read_all_as_string(1), "LhPnfEG+0rk+Ej6y");
1✔
280
         result.test_str_eq("MAC 3", pipe.read_all_as_string(2), "e7NoVbtudgU0QiCZ");
1✔
281
   #endif
282
         return result;
2✔
283
      }
2✔
284

285
      static Test::Result test_pipe_hash() {
1✔
286
         Test::Result result("Pipe");
1✔
287

288
   #if defined(BOTAN_HAS_SHA2_32)
289
         // unrelated test of popping a chain
290
         Botan::Pipe pipe(new Botan::Chain(new Botan::Hash_Filter("SHA-224"), new Botan::Hash_Filter("SHA-224")));
1✔
291
         pipe.pop();
1✔
292

293
         pipe.append(new Botan::Hash_Filter("SHA-256"));
1✔
294

295
         result.test_sz_eq("Message count", pipe.message_count(), 0);
1✔
296

297
         pipe.start_msg();
1✔
298
         const uint8_t inb = 0x41;
1✔
299
         pipe.write(&inb, 1);
1✔
300
         pipe.write(std::vector<uint8_t>(6, 0x41));
2✔
301
         pipe.write(inb);
1✔
302
         pipe.end_msg();
1✔
303

304
         result.test_sz_eq("Message count", pipe.message_count(), 1);
1✔
305
         result.test_sz_eq("Message size", pipe.remaining(), 32);
1✔
306

307
         std::vector<uint8_t> out(32);
1✔
308
         std::vector<uint8_t> last16(16);
1✔
309

310
         result.test_sz_eq("Bytes read", pipe.get_bytes_read(0), 0);
1✔
311
         result.test_is_false("More to read", pipe.end_of_data());
1✔
312
         result.test_sz_eq("Expected read count", pipe.read(out.data(), 5), 5);
1✔
313
         result.test_sz_eq("Bytes read", pipe.get_bytes_read(0), 5);
1✔
314
         result.test_sz_eq("Peek read", pipe.peek(last16.data(), 18, 11), 16);
1✔
315
         result.test_sz_eq("Expected read count", pipe.read(&out[5], 17), 17);
1✔
316
         result.test_sz_eq("Bytes read", pipe.get_bytes_read(0), 22);
1✔
317
         result.test_sz_eq("Remaining", pipe.remaining(), 10);
1✔
318
         result.test_sz_eq("Remaining", pipe.remaining(), 10);
1✔
319
         result.test_sz_eq("Expected read count", pipe.read(&out[22], 12), 10);
1✔
320
         result.test_sz_eq("Expected read count", pipe.read(out.data(), 1), 0);  // no more output
1✔
321
         result.test_sz_eq("Bytes read", pipe.get_bytes_read(0), 32);
1✔
322
         result.test_is_true("No more to read", pipe.end_of_data());
1✔
323

324
         result.test_bin_eq("Expected output", out, "C34AB6ABB7B2BB595BC25C3B388C872FD1D575819A8F55CC689510285E212385");
1✔
325
         result.test_bin_eq("Expected last16", last16, "D1D575819A8F55CC689510285E212385");
1✔
326

327
         pipe.reset();
1✔
328

329
      #if defined(BOTAN_HAS_CRC32)
330
         pipe.prepend(new Botan::Hash_Filter("CRC32"));
1✔
331
         pipe.append(new Botan::Hash_Filter("CRC32"));
1✔
332
         pipe.process_msg(std::vector<uint8_t>(1024, 0));
1✔
333
         result.test_bin_eq("Expected CRC32d", pipe.read_all(1), "99841F60");
1✔
334
      #endif
335
   #endif
336
         return result;
2✔
337
      }
2✔
338

339
      static Test::Result test_pipe_cfb() {
1✔
340
         Test::Result result("Pipe CFB");
1✔
341

342
   #if defined(BOTAN_HAS_BLOWFISH) && defined(BOTAN_HAS_MODE_CFB) && defined(BOTAN_HAS_CODEC_FILTERS)
343

344
         // Generated with Botan 1.10
345

346
         const Botan::InitializationVector iv("AABBCCDDEEFF0123");
1✔
347
         const Botan::SymmetricKey key("AABBCCDDEEFF0123");
1✔
348

349
         const uint8_t msg_bits[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
1✔
350

351
         const std::string cfb_expected[] = {
1✔
352
            "A4",
353
            "BEA4",
354
            "06AD98",
355
            "E4AFC5AC",
356
            "A9B531559C",
357
            "38B60DA66445",
358
            "194F5E93199839",
359
            "093B6381D2E5D806",
360
            "B44FA624226EECF027",
361
            "80B8DC3332A835AC11A8",
362
            "2C0E910A1E5C38344CC5BB",
363
            "3CB6180AE2E189342F681023",
364
            "DE0F4B10C7D9CADDB5A9078199",
365
            "FAE18B0ED873F234CCD6E1555B2D",
366
            "7195FFE735B0A95065BA244C77A11F",
367
         };
16✔
368

369
         Botan::Keyed_Filter* cfb_enc = Botan::get_cipher("Blowfish/CFB", key, iv, Botan::Cipher_Dir::Encryption);
1✔
370
         Botan::Pipe enc_pipe(cfb_enc, new Botan::Hex_Encoder);
1✔
371

372
         Botan::Keyed_Filter* cfb_dec = Botan::get_cipher("Blowfish/CFB", key, Botan::Cipher_Dir::Decryption);
1✔
373
         cfb_dec->set_iv(iv);
1✔
374
         Botan::Pipe dec_pipe(new Botan::Hex_Decoder, cfb_dec, new Botan::Hex_Encoder);
1✔
375

376
         for(size_t i = 1; i != sizeof(msg_bits); ++i) {
15✔
377
            enc_pipe.start_msg();
14✔
378
            enc_pipe.write(msg_bits, i);
14✔
379
            enc_pipe.end_msg();
14✔
380

381
            dec_pipe.process_msg(cfb_expected[i - 1]);
14✔
382
         }
383

384
         result.test_sz_eq("enc pipe msg count", enc_pipe.message_count(), sizeof(msg_bits) - 1);
1✔
385
         result.test_sz_eq("dec pipe msg count", dec_pipe.message_count(), sizeof(msg_bits) - 1);
1✔
386

387
         for(size_t i = 0; i != enc_pipe.message_count(); ++i) {
15✔
388
            result.test_str_eq("encrypt", enc_pipe.read_all_as_string(i), cfb_expected[i]);
14✔
389
         }
390

391
         for(size_t i = 0; i != dec_pipe.message_count(); ++i) {
15✔
392
            result.test_str_eq("decrypt", dec_pipe.read_all_as_string(i), Botan::hex_encode(msg_bits, i + 1));
14✔
393
         }
394
   #endif
395

396
         return result;
2✔
397
      }
18✔
398

399
      static Test::Result test_pipe_cbc() {
1✔
400
         Test::Result result("Pipe CBC");
1✔
401

402
   #if defined(BOTAN_HAS_AES) && defined(BOTAN_HAS_MODE_CBC) && defined(BOTAN_HAS_CIPHER_MODE_PADDING)
403
         Botan::Cipher_Mode_Filter* cipher = new Botan::Cipher_Mode_Filter(
1✔
404
            Botan::Cipher_Mode::create("AES-128/CBC/PKCS7", Botan::Cipher_Dir::Encryption));
2✔
405

406
         result.test_str_eq("Cipher filter name", cipher->name(), "AES-128/CBC/PKCS7");
1✔
407

408
         result.test_is_true("Cipher filter nonce size", cipher->valid_iv_length(16));
1✔
409
         result.test_is_false("Cipher filter nonce size", cipher->valid_iv_length(17));
1✔
410

411
         result.test_sz_eq("Cipher key length max", cipher->key_spec().maximum_keylength(), 16);
1✔
412
         result.test_sz_eq("Cipher key length min", cipher->key_spec().minimum_keylength(), 16);
1✔
413

414
         // takes ownership of cipher
415
         Botan::Pipe pipe(cipher);
1✔
416

417
         cipher->set_key(Botan::SymmetricKey("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
1✔
418
         cipher->set_iv(Botan::InitializationVector("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
1✔
419

420
         pipe.process_msg("Don't use plain CBC mode");
1✔
421

422
         result.test_sz_eq("Message count", pipe.message_count(), 1);
1✔
423
         result.test_sz_eq("Bytes read", pipe.get_bytes_read(), 0);
1✔
424
         auto ciphertext = pipe.read_all();
1✔
425
         result.test_sz_eq("Bytes read after", pipe.get_bytes_read(), ciphertext.size());
1✔
426

427
         result.test_bin_eq(
1✔
428
            "Ciphertext", ciphertext, "9BDD7300E0CB61CA71FFF957A71605DB 6836159C36781246A1ADF50982757F4B");
429

430
         pipe.process_msg("IV carryover");
1✔
431
         auto ciphertext2 = pipe.read_all(1);
1✔
432
         pipe.process_msg("IV carryover2");
1✔
433
         auto ciphertext3 = pipe.read_all(2);
1✔
434

435
         // These values tested against PyCrypto
436
         result.test_bin_eq("Ciphertext2", ciphertext2, "AA8D682958A4A044735DAC502B274DB2");
1✔
437
         result.test_bin_eq("Ciphertext3", ciphertext3, "1241B9976F73051BCF809525D6E86C25");
1✔
438

439
         Botan::Cipher_Mode_Filter* dec_cipher = new Botan::Cipher_Mode_Filter(
1✔
440
            Botan::Cipher_Mode::create("AES-128/CBC/PKCS7", Botan::Cipher_Dir::Decryption));
2✔
441
         pipe.append(dec_cipher);
1✔
442
         dec_cipher->set_key(Botan::SymmetricKey("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
1✔
443
         dec_cipher->set_iv(Botan::InitializationVector("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB"));
1✔
444

445
         // reset the IV on the encryption filter
446
         cipher->set_iv(Botan::InitializationVector("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB"));
1✔
447

448
         const std::vector<uint8_t> zeros_in(1024);
1✔
449
         Botan::DataSource_Memory src(zeros_in);
1✔
450
         pipe.start_msg();
1✔
451
         pipe.write(src);
1✔
452
         pipe.end_msg();
1✔
453

454
         pipe.set_default_msg(3);
1✔
455
         result.test_sz_eq("Bytes read", pipe.get_bytes_read(), 0);
1✔
456
         Botan::secure_vector<uint8_t> zeros_out = pipe.read_all();
1✔
457
         result.test_sz_eq("Bytes read", pipe.get_bytes_read(), zeros_out.size());
1✔
458

459
         result.test_bin_eq("Cipher roundtrip", zeros_in, zeros_out);
1✔
460
   #endif
461
         return result;
2✔
462
      }
6✔
463

464
      static Test::Result test_pipe_compress() {
1✔
465
         Test::Result result("Pipe compress zlib");
1✔
466

467
   #if defined(BOTAN_HAS_ZLIB)
468

469
         auto comp_f = std::make_unique<Botan::Compression_Filter>("zlib", 9);
1✔
470

471
         result.test_str_eq("Compressor filter name", comp_f->name(), "Zlib_Compression");
1✔
472
         Botan::Pipe pipe(comp_f.release());
1✔
473

474
         const std::string input_str = "Hello there HELLO there I said is this thing on?";
1✔
475

476
         pipe.start_msg();
1✔
477
         pipe.write(input_str);
1✔
478
         pipe.end_msg();
1✔
479

480
         auto compr = pipe.read_all(0);
1✔
481
         // Can't do equality check on compression because output may differ
482
         result.test_sz_lt("Compressed is shorter", compr.size(), input_str.size());
1✔
483

484
         auto decomp_f = std::make_unique<Botan::Decompression_Filter>("zlib");
1✔
485
         result.test_str_eq("Decompressor name", decomp_f->name(), "Zlib_Decompression");
1✔
486
         pipe.append(decomp_f.release());
1✔
487
         pipe.pop();  // remove compressor
1✔
488

489
         pipe.process_msg(compr);
1✔
490

491
         const std::string decomp = pipe.read_all_as_string(1);
1✔
492
         result.test_str_eq("Decompressed ok", decomp, input_str);
1✔
493
   #endif
494

495
         return result;
2✔
496
      }
2✔
497

498
      static Test::Result test_pipe_compress_bzip2() {
1✔
499
         Test::Result result("Pipe compress bzip2");
1✔
500

501
   #if defined(BOTAN_HAS_BZIP2)
502

503
         auto comp_f = std::make_unique<Botan::Compression_Filter>("bzip2", 9);
1✔
504

505
         result.test_str_eq("Compressor filter name", comp_f->name(), "Bzip2_Compression");
1✔
506
         Botan::Pipe pipe(comp_f.release());
1✔
507

508
         const std::string input_str = "foo\n";
1✔
509

510
         pipe.start_msg();
1✔
511
         pipe.write(input_str);
1✔
512
         pipe.end_msg();
1✔
513

514
         auto compr = pipe.read_all(0);
1✔
515
         // Here the output is actually longer than the input as input is so short
516

517
         auto decomp_f = std::make_unique<Botan::Decompression_Filter>("bzip2");
1✔
518
         result.test_str_eq("Decompressor name", decomp_f->name(), "Bzip2_Decompression");
1✔
519
         pipe.append(decomp_f.release());
1✔
520
         pipe.pop();  // remove compressor
1✔
521

522
         pipe.process_msg(compr);
1✔
523

524
         const std::string decomp = pipe.read_all_as_string(1);
1✔
525
         result.test_str_eq("Decompressed ok", decomp, input_str);
1✔
526
   #endif
527

528
         return result;
2✔
529
      }
2✔
530

531
      static Test::Result test_pipe_codec() {
1✔
532
         Test::Result result("Pipe");
1✔
533

534
   #if defined(BOTAN_HAS_CODEC_FILTERS)
535
         Botan::Pipe pipe(new Botan::Base64_Encoder);
1✔
536

537
         result.test_sz_eq("Message count", pipe.message_count(), 0);
1✔
538

539
         pipe.process_msg("ABCDX");
1✔
540

541
         result.test_sz_eq("Message count", pipe.message_count(), 1);
1✔
542
         result.test_sz_eq("Message size", pipe.remaining(), 8);
1✔
543

544
         const std::string output = pipe.read_all_as_string(0);
1✔
545
         result.test_sz_eq("Message size", pipe.remaining(0), 0);
1✔
546
         result.test_str_eq("Output round tripped", output, "QUJDRFg=");
1✔
547

548
         pipe.append(new Botan::Base64_Decoder);
1✔
549
         pipe.process_msg("FOOBAZ");
1✔
550

551
         result.test_str_eq("base64 roundtrip", pipe.read_all_as_string(1), "FOOBAZ");
1✔
552

553
         pipe.pop();
1✔
554
         pipe.pop();
1✔
555

556
         // Pipe is empty of filters, should still pass through
557
         pipe.process_msg("surprise plaintext");
1✔
558

559
         pipe.set_default_msg(2);
1✔
560
         result.test_str_eq("Message 2", pipe.read_all_as_string(), "surprise plaintext");
1✔
561

562
         pipe.append(new Botan::Hex_Decoder);
1✔
563

564
         pipe.process_msg("F331F00D");
1✔
565
         Botan::secure_vector<uint8_t> bin = pipe.read_all(3);
1✔
566
         result.test_bin_eq("hex decoded", bin, "F331F00D");
1✔
567

568
         pipe.append(new Botan::Hex_Encoder);
1✔
569
         pipe.process_msg("F331F00D");
1✔
570
         result.test_str_eq("hex roundtrip", pipe.read_all_as_string(4), "F331F00D");
1✔
571

572
         // Now tests with line wrapping enabled
573

574
         pipe.reset();
1✔
575
         pipe.append(new Botan::Hex_Decoder);
1✔
576
         pipe.append(new Botan::Base64_Encoder(/*line_breaks=*/true,
1✔
577
                                               /*line_length=*/4,
578
                                               /*trailing_newline=*/true));
1✔
579

580
         pipe.process_msg("6dab1eeb8a2eb69bad");
1✔
581
         result.test_str_eq(
1✔
582
            "base64 with linebreaks and trailing newline", pipe.read_all_as_string(5), "base\n64ou\ntput\n\n");
1✔
583

584
         pipe.reset();
1✔
585
         pipe.append(new Botan::Hex_Decoder);
1✔
586
         pipe.append(new Botan::Base64_Encoder(true, 5, false));
1✔
587
         pipe.process_msg("6dab1eeb8a2eb69bad");
1✔
588
         result.test_str_eq("base64 with linebreaks", pipe.read_all_as_string(6), "base6\n4outp\nut\n");
1✔
589

590
         pipe.reset();
1✔
591
         pipe.append(new Botan::Hex_Encoder(true, 13, Botan::Hex_Encoder::Uppercase));
1✔
592
         pipe.process_msg("hex encoding this string");
1✔
593
         result.test_str_eq("hex uppercase with linebreaks",
1✔
594
                            pipe.read_all_as_string(7),
1✔
595
                            "68657820656E6\n36F64696E6720\n7468697320737\n472696E67\n");
596

597
         pipe.reset();
1✔
598
         pipe.append(new Botan::Hex_Encoder(true, 16, Botan::Hex_Encoder::Lowercase));
1✔
599
         pipe.process_msg("hex encoding this string");
1✔
600
         result.test_str_eq("hex lowercase with linebreaks",
1✔
601
                            pipe.read_all_as_string(8),
1✔
602
                            "68657820656e636f\n64696e6720746869\n7320737472696e67\n");
603
   #endif
604

605
         return result;
2✔
606
      }
1✔
607

608
      static Test::Result test_pipe_stream() {
1✔
609
         Test::Result result("Pipe CTR");
1✔
610

611
   #if defined(BOTAN_HAS_CTR_BE) && defined(BOTAN_HAS_AES)
612
         Botan::Keyed_Filter* aes = nullptr;
1✔
613
         const Botan::SymmetricKey some_other_key("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE");
1✔
614
         const Botan::SymmetricKey key("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
1✔
615
         const Botan::InitializationVector iv("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
1✔
616
         Botan::Pipe pipe(aes = new Botan::StreamCipher_Filter("CTR-BE(AES-128)", some_other_key));
1✔
617

618
         aes->set_key(key);
1✔
619
         aes->set_iv(iv);
1✔
620

621
         pipe.process_msg("ABCDEF");
1✔
622

623
         result.test_sz_eq("Message count", pipe.message_count(), 1);
1✔
624
         result.test_bin_eq("Ciphertext", pipe.read_all(), "FDFD6238F7C6");
1✔
625

626
         pipe.process_msg("ABCDEF");
1✔
627
         result.test_bin_eq("Ciphertext", pipe.read_all(1), "8E72F1153514");
1✔
628
   #endif
629
         return result;
2✔
630
      }
4✔
631

632
      static Test::Result test_fork() {
1✔
633
         Test::Result result("Filter Fork");
1✔
634

635
   #if defined(BOTAN_HAS_SHA2_32) && defined(BOTAN_HAS_SHA2_64)
636
         Botan::Pipe pipe(new Botan::Fork(new Botan::Hash_Filter("SHA-256"), new Botan::Hash_Filter("SHA-512-256")));
1✔
637

638
         result.test_sz_eq("Message count", pipe.message_count(), 0);
1✔
639
         pipe.process_msg("OMG");
1✔
640
         result.test_sz_eq("Message count", pipe.message_count(), 2);
1✔
641

642
         // Test reading out of order
643
         result.test_bin_eq(
1✔
644
            "Hash 2", pipe.read_all(1), "610480FFA82F24F6926544B976FE387878E3D973C03DFD591C2E9896EFB903E0");
1✔
645
         result.test_bin_eq(
1✔
646
            "Hash 1", pipe.read_all(0), "C00862D1C6C1CF7C1B49388306E7B3C1BB79D8D6EC978B41035B556DBB3797DF");
1✔
647
   #endif
648
         return result;
1✔
649
      }
1✔
650

651
      static Test::Result test_chain() {
1✔
652
         Test::Result result("Filter Chain");
1✔
653

654
   #if defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_HAS_SHA2_32) && defined(BOTAN_HAS_SHA2_64)
655

656
         // NOLINTNEXTLINE(*-const-correctness) bug in clang-tidy
657
         Botan::Filter* filters[2] = {new Botan::Hash_Filter("SHA-256"), new Botan::Hex_Encoder};
1✔
658

659
         auto chain = std::make_unique<Botan::Chain>(filters, 2);
1✔
660

661
         result.test_str_eq("Chain has a name", chain->name(), "Chain");
1✔
662

663
         auto fork = std::make_unique<Botan::Fork>(
1✔
664
            chain.release(), new Botan::Chain(new Botan::Hash_Filter("SHA-512-256", 19), new Botan::Hex_Encoder));
1✔
665

666
         result.test_str_eq("Fork has a name", fork->name(), "Fork");
1✔
667
         Botan::Pipe pipe(fork.release());
1✔
668

669
         result.test_sz_eq("Message count", pipe.message_count(), 0);
1✔
670
         pipe.process_msg("OMG");
1✔
671
         result.test_sz_eq("Message count", pipe.message_count(), 2);
1✔
672

673
         result.test_str_eq(
1✔
674
            "Hash 1", pipe.read_all_as_string(0), "C00862D1C6C1CF7C1B49388306E7B3C1BB79D8D6EC978B41035B556DBB3797DF");
1✔
675
         result.test_str_eq("Hash 2", pipe.read_all_as_string(1), "610480FFA82F24F6926544B976FE387878E3D9");
1✔
676
   #endif
677

678
         return result;
1✔
679
      }
1✔
680

681
      static Test::Result test_pipe_fd_io() {
1✔
682
         Test::Result result("Pipe file descriptor IO");
1✔
683

684
   #if defined(BOTAN_HAS_PIPE_UNIXFD_IO) && defined(BOTAN_HAS_CODEC_FILTERS) && !defined(BOTAN_TARGET_OS_IS_EMSCRIPTEN)
685
         int fd[2];
1✔
686
         if(::pipe(fd) != 0) {
1✔
687
            return result;  // pipe unavailable?
688
         }
689

690
         Botan::Pipe hex_enc(new Botan::Hex_Encoder);
1✔
691
         Botan::Pipe hex_dec(new Botan::Hex_Decoder);
1✔
692

693
         hex_enc.process_msg("hi chappy");
1✔
694
         fd[1] << hex_enc;
1✔
695
         ::close(fd[1]);
1✔
696

697
         hex_dec.start_msg();
1✔
698
         fd[0] >> hex_dec;
1✔
699
         hex_dec.end_msg();
1✔
700
         ::close(fd[0]);
1✔
701

702
         const std::string dec = hex_dec.read_all_as_string();
1✔
703

704
         result.test_str_eq("IO through Unix pipe works", dec, "hi chappy");
1✔
705
   #endif
706

707
         return result;
1✔
708
      }
1✔
709

710
      static Test::Result test_threaded_fork() {
1✔
711
         Test::Result result("Threaded_Fork");
1✔
712

713
   #if defined(BOTAN_HAS_THREAD_UTILS) && defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_HAS_SHA2_32)
714
         Botan::Pipe pipe(new Botan::Threaded_Fork(new Botan::Hex_Encoder, new Botan::Base64_Encoder));
1✔
715

716
         result.test_sz_eq("Message count", pipe.message_count(), 0);
1✔
717
         pipe.process_msg("woo");
1✔
718
         result.test_sz_eq("Message count", pipe.message_count(), 2);
1✔
719

720
         // Test reading out of order
721
         result.test_str_eq("Hash 2", pipe.read_all_as_string(1), "d29v");
1✔
722
         result.test_str_eq("Hash 1", pipe.read_all_as_string(0), "776F6F");
1✔
723

724
         pipe.reset();
1✔
725

726
         const size_t filter_count = 5;
727
         Botan::Filter* filters[filter_count];
728
         for(auto& filter : filters) {
6✔
729
            filter = new Botan::Hash_Filter("SHA-256");
5✔
730
         }
731

732
         pipe.append(new Botan::Threaded_Fork(filters, filter_count));
1✔
733

734
         result.test_sz_eq("Message count before start_msg", pipe.message_count(), 2);
1✔
735

736
         pipe.start_msg();
1✔
737
         for(size_t i = 0; i != 919; ++i) {
920✔
738
            const std::vector<uint8_t> input(i + 5, static_cast<uint8_t>(i));
919✔
739
            pipe.write(input);
919✔
740
         }
919✔
741
         pipe.end_msg();
1✔
742

743
         result.test_sz_eq("Message count after end_msg", pipe.message_count(), 2 + filter_count);
1✔
744
         for(size_t i = 0; i != filter_count; ++i) {
6✔
745
            result.test_bin_eq(
5✔
746
               "Output", pipe.read_all(2 + i), "327AD8055223F5926693D8BEA40F7B35BDEEB535647DFB93F464E40EA01939A9");
10✔
747
         }
748
   #endif
749
         return result;
1✔
750
      }
1✔
751
};
752

753
// NOLINTEND(*-owning-memory)
754

755
BOTAN_REGISTER_TEST("filters", "filter", Filter_Tests);
756

757
#endif
758

759
}  // namespace
760

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