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

randombit / botan / 22045109103

15 Feb 2026 11:27PM UTC coverage: 90.043% (-0.01%) from 90.054%
22045109103

push

github

web-flow
Merge pull request #5342 from randombit/jack/test-h-arb-eq

Rename test_is_eq to test_arb_eq and require the type be printable

102325 of 113640 relevant lines covered (90.04%)

11463137.71 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
#if defined(BOTAN_HAS_FILTERS)
34

35
// NOLINTBEGIN(*-owning-memory)
36

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

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

60
         return results;
1✔
61
      }
×
62

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

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

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

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

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

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

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

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

96
         return result;
1✔
97
      }
×
98

99
      static Test::Result test_data_src_sink() {
1✔
100
         Test::Result result("DataSink");
1✔
101

102
   #if defined(BOTAN_HAS_CODEC_FILTERS)
103
         std::ostringstream oss;
1✔
104

105
         Botan::Pipe pipe(new Botan::Hex_Decoder, new Botan::DataSink_Stream(oss));
1✔
106

107
         Botan::DataSource_Memory input_mem("65666768");
1✔
108
         pipe.process_msg(input_mem);
1✔
109

110
         result.test_str_eq("output string", oss.str(), "efgh");
1✔
111

112
         Botan::DataSource_Memory input_mem2("41414141");
1✔
113
         pipe.process_msg(input_mem2);
1✔
114

115
         result.test_str_eq("output string", oss.str(), "efghAAAA");
1✔
116

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

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

126
      static Test::Result test_data_src_sink_flush() {
1✔
127
         Test::Result result("DataSinkFlush");
1✔
128

129
   #if defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
130

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

137
         std::ofstream outfile(tmp_name);
1✔
138

139
         Botan::Pipe pipe(new Botan::Hex_Decoder, new Botan::DataSink_Stream(outfile));
1✔
140

141
         Botan::DataSource_Memory input_mem("65666768");
1✔
142
         pipe.process_msg(input_mem);
1✔
143

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

148
         result.test_str_eq("output string", ss.str(), "efgh");
1✔
149

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

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

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

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

167
         pipe.process_msg("ABCD");
1✔
168

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

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

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

183
         return result;
1✔
184
      }
1✔
185

186
      static Test::Result test_pipe_errors() {
1✔
187
         Test::Result result("Pipe");
1✔
188

189
         Botan::Pipe pipe;
1✔
190

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

197
         auto queue_filter = std::make_shared<Botan::SecureQueue>();
1✔
198

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

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

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

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

212
         pipe.start_msg();
1✔
213

214
         auto filter = std::make_unique<Botan::BitBucket>();
1✔
215

216
         // now inside a message, cannot modify pipe structure
217

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

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

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

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

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

234
         pipe.end_msg();
1✔
235

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

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

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

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

253
         return result;
2✔
254
      }
2✔
255

256
      static Test::Result test_pipe_mac() {
1✔
257
         Test::Result result("Pipe");
1✔
258

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

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

264
         mac_filter->set_iv(Botan::InitializationVector());  // ignored
1✔
265

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

270
         Botan::Pipe pipe(mac_filter, new Botan::Base64_Encoder);
1✔
271

272
         pipe.process_msg("Hi");
1✔
273
         pipe.process_msg("Bye");
1✔
274
         pipe.process_msg("Hi");
1✔
275

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

283
      static Test::Result test_pipe_hash() {
1✔
284
         Test::Result result("Pipe");
1✔
285

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

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

293
         result.test_sz_eq("Message count", pipe.message_count(), 0);
1✔
294

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

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

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

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

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

325
         pipe.reset();
1✔
326

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

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

340
   #if defined(BOTAN_HAS_BLOWFISH) && defined(BOTAN_HAS_MODE_CFB) && defined(BOTAN_HAS_CODEC_FILTERS)
341

342
         // Generated with Botan 1.10
343

344
         const Botan::InitializationVector iv("AABBCCDDEEFF0123");
1✔
345
         const Botan::SymmetricKey key("AABBCCDDEEFF0123");
1✔
346

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

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

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

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

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

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

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

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

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

394
         return result;
2✔
395
      }
18✔
396

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

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

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

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

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

412
         // takes ownership of cipher
413
         Botan::Pipe pipe(cipher);
1✔
414

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

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

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

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

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

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

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

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

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

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

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

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

465
   #if defined(BOTAN_HAS_ZLIB)
466

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

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

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

474
         pipe.start_msg();
1✔
475
         pipe.write(input_str);
1✔
476
         pipe.end_msg();
1✔
477

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

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

487
         pipe.process_msg(compr);
1✔
488

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

493
         return result;
2✔
494
      }
2✔
495

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

499
   #if defined(BOTAN_HAS_BZIP2)
500

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

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

506
         const std::string input_str = "foo\n";
1✔
507

508
         pipe.start_msg();
1✔
509
         pipe.write(input_str);
1✔
510
         pipe.end_msg();
1✔
511

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

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

520
         pipe.process_msg(compr);
1✔
521

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

526
         return result;
2✔
527
      }
2✔
528

529
      static Test::Result test_pipe_codec() {
1✔
530
         Test::Result result("Pipe");
1✔
531

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

535
         result.test_sz_eq("Message count", pipe.message_count(), 0);
1✔
536

537
         pipe.process_msg("ABCDX");
1✔
538

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

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

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

549
         result.test_str_eq("base64 roundtrip", pipe.read_all_as_string(1), "FOOBAZ");
1✔
550

551
         pipe.pop();
1✔
552
         pipe.pop();
1✔
553

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

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

560
         pipe.append(new Botan::Hex_Decoder);
1✔
561

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

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

570
         // Now tests with line wrapping enabled
571

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

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

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

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

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

603
         return result;
2✔
604
      }
1✔
605

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

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

616
         aes->set_key(key);
1✔
617
         aes->set_iv(iv);
1✔
618

619
         pipe.process_msg("ABCDEF");
1✔
620

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

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

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

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

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

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

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

652
   #if defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_HAS_SHA2_32) && defined(BOTAN_HAS_SHA2_64)
653

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

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

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

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

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

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

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

676
         return result;
1✔
677
      }
1✔
678

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

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

688
         Botan::Pipe hex_enc(new Botan::Hex_Encoder);
1✔
689
         Botan::Pipe hex_dec(new Botan::Hex_Decoder);
1✔
690

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

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

700
         const std::string dec = hex_dec.read_all_as_string();
1✔
701

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

705
         return result;
1✔
706
      }
1✔
707

708
      static Test::Result test_threaded_fork() {
1✔
709
         Test::Result result("Threaded_Fork");
1✔
710

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

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

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

722
         pipe.reset();
1✔
723

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

730
         pipe.append(new Botan::Threaded_Fork(filters, filter_count));
1✔
731

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

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

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

751
// NOLINTEND(*-owning-memory)
752

753
BOTAN_REGISTER_TEST("filters", "filter", Filter_Tests);
754

755
#endif
756

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