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

randombit / botan / 21783203606

07 Feb 2026 04:30PM UTC coverage: 90.068% (-0.005%) from 90.073%
21783203606

Pull #5295

github

web-flow
Merge a6c023b97 into ebf8f0044
Pull Request #5295: Reduce header dependencies in tests and cli

102233 of 113507 relevant lines covered (90.07%)

11542227.95 hits per line

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

98.17
/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
#endif
20

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

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

30
namespace Botan_Tests {
31

32
#if defined(BOTAN_HAS_FILTERS)
33

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

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

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

59
         return results;
1✔
60
      }
×
61

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

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

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

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

76
            result.test_eq("check_available", queue_a.check_available(1), true);
1✔
77
            result.test_eq("check_available", queue_a.check_available(50), false);
1✔
78
            uint8_t b = 0;
1✔
79
            const size_t bytes_read = queue_a.read_byte(b);
1✔
80
            result.test_eq("1 byte read", bytes_read, 1);
1✔
81

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

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

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

95
         return result;
1✔
96
      }
×
97

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

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

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

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

109
         result.test_eq("output string", oss.str(), "efgh");
3✔
110

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

114
         result.test_eq("output string", oss.str(), "efghAAAA");
3✔
115

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

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

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

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

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

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

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

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

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

147
         result.test_eq("output string", ss.str(), "efgh");
3✔
148

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

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

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

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

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

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

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

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

182
         return result;
1✔
183
      }
1✔
184

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

188
         Botan::Pipe pipe;
1✔
189

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

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

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

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

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

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

211
         pipe.start_msg();
1✔
212

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

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

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

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

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

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

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

233
         pipe.end_msg();
1✔
234

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

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

243
         result.test_throws("pipe error", "Pipe::read: Invalid message number 100", [&]() {
2✔
244
            uint8_t b = 0;
1✔
245
            const size_t got = pipe.read(&b, 1, 100);
1✔
246
            BOTAN_UNUSED(got);
×
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",
2✔
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_eq("MAC 1", pipe.read_all_as_string(0), "e7NoVbtudgU0QiCZ");
2✔
277
         result.test_eq("MAC 2", pipe.read_all_as_string(1), "LhPnfEG+0rk+Ej6y");
2✔
278
         result.test_eq("MAC 3", pipe.read_all_as_string(2), "e7NoVbtudgU0QiCZ");
2✔
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_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_eq("Message count", pipe.message_count(), 1);
1✔
303
         result.test_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_eq("Bytes read", pipe.get_bytes_read(0), 0);
1✔
309
         result.test_eq("More to read", pipe.end_of_data(), false);
1✔
310
         result.test_eq("Expected read count", pipe.read(out.data(), 5), 5);
1✔
311
         result.test_eq("Bytes read", pipe.get_bytes_read(0), 5);
1✔
312
         result.test_eq("Peek read", pipe.peek(last16.data(), 18, 11), 16);
1✔
313
         result.test_eq("Expected read count", pipe.read(&out[5], 17), 17);
1✔
314
         result.test_eq("Bytes read", pipe.get_bytes_read(0), 22);
1✔
315
         result.test_eq("Remaining", pipe.remaining(), 10);
1✔
316
         result.test_eq("Remaining", pipe.remaining(), 10);
1✔
317
         result.test_eq("Expected read count", pipe.read(&out[22], 12), 10);
1✔
318
         result.test_eq("Expected read count", pipe.read(out.data(), 1), 0);  // no more output
1✔
319
         result.test_eq("Bytes read", pipe.get_bytes_read(0), 32);
1✔
320
         result.test_eq("No more to read", pipe.end_of_data(), true);
1✔
321

322
         result.test_eq("Expected output", out, "C34AB6ABB7B2BB595BC25C3B388C872FD1D575819A8F55CC689510285E212385");
1✔
323
         result.test_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_eq("Expected CRC32d", pipe.read_all(1), "99841F60");
2✔
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_eq("enc pipe msg count", enc_pipe.message_count(), sizeof(msg_bits) - 1);
1✔
383
         result.test_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_eq("encrypt", enc_pipe.read_all_as_string(i), cfb_expected[i]);
28✔
387
         }
388

389
         for(size_t i = 0; i != dec_pipe.message_count(); ++i) {
15✔
390
            result.test_eq("decrypt", dec_pipe.read_all_as_string(i), Botan::hex_encode(msg_bits, i + 1));
28✔
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_eq("Cipher filter name", cipher->name(), "AES-128/CBC/PKCS7");
2✔
405

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

409
         result.test_eq("Cipher key length max", cipher->key_spec().maximum_keylength(), 16);
1✔
410
         result.test_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_eq("Message count", pipe.message_count(), 1);
1✔
421
         result.test_eq("Bytes read", pipe.get_bytes_read(), 0);
1✔
422
         auto ciphertext = pipe.read_all();
1✔
423
         result.test_eq("Bytes read after", pipe.get_bytes_read(), ciphertext.size());
1✔
424

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

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

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

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

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

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

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

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

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

464
   #if defined(BOTAN_HAS_ZLIB)
465

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

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

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

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

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

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

486
         pipe.process_msg(compr);
1✔
487

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

492
         return result;
2✔
493
      }
2✔
494

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

498
   #if defined(BOTAN_HAS_BZIP2)
499

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

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

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

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

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

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

519
         pipe.process_msg(compr);
1✔
520

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

525
         return result;
2✔
526
      }
2✔
527

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

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

534
         result.test_eq("Message count", pipe.message_count(), 0);
1✔
535

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

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

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

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

548
         result.test_eq("base64 roundtrip", pipe.read_all_as_string(1), "FOOBAZ");
2✔
549

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

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

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

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

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

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

569
         // Now tests with line wrapping enabled
570

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

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

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

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

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

602
         return result;
2✔
603
      }
1✔
604

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

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

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

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

620
         result.test_eq("Message count", pipe.message_count(), 1);
1✔
621
         result.test_eq("Ciphertext", pipe.read_all(), "FDFD6238F7C6");
2✔
622

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

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

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

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

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

646
      static Test::Result test_chain() {
1✔
647
         Test::Result result("Filter Chain");
1✔
648

649
   #if defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_HAS_SHA2_32) && defined(BOTAN_HAS_SHA2_64)
650

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

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

656
         result.test_eq("Chain has a name", chain->name(), "Chain");
2✔
657

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

661
         result.test_eq("Fork has a name", fork->name(), "Fork");
2✔
662
         Botan::Pipe pipe(fork.release());
1✔
663

664
         result.test_eq("Message count", pipe.message_count(), 0);
1✔
665
         pipe.process_msg("OMG");
1✔
666
         result.test_eq("Message count", pipe.message_count(), 2);
1✔
667

668
         result.test_eq(
3✔
669
            "Hash 1", pipe.read_all_as_string(0), "C00862D1C6C1CF7C1B49388306E7B3C1BB79D8D6EC978B41035B556DBB3797DF");
2✔
670
         result.test_eq("Hash 2", pipe.read_all_as_string(1), "610480FFA82F24F6926544B976FE387878E3D9");
2✔
671
   #endif
672

673
         return result;
1✔
674
      }
1✔
675

676
      static Test::Result test_pipe_fd_io() {
1✔
677
         Test::Result result("Pipe file descriptor IO");
1✔
678

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

685
         Botan::Pipe hex_enc(new Botan::Hex_Encoder);
1✔
686
         Botan::Pipe hex_dec(new Botan::Hex_Decoder);
1✔
687

688
         hex_enc.process_msg("hi chappy");
1✔
689
         fd[1] << hex_enc;
1✔
690
         ::close(fd[1]);
1✔
691

692
         hex_dec.start_msg();
1✔
693
         fd[0] >> hex_dec;
1✔
694
         hex_dec.end_msg();
1✔
695
         ::close(fd[0]);
1✔
696

697
         const std::string dec = hex_dec.read_all_as_string();
1✔
698

699
         result.test_eq("IO through Unix pipe works", dec, "hi chappy");
2✔
700
   #endif
701

702
         return result;
1✔
703
      }
1✔
704

705
      static Test::Result test_threaded_fork() {
1✔
706
         Test::Result result("Threaded_Fork");
1✔
707

708
   #if defined(BOTAN_HAS_THREAD_UTILS) && defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_HAS_SHA2_32)
709
         Botan::Pipe pipe(new Botan::Threaded_Fork(new Botan::Hex_Encoder, new Botan::Base64_Encoder));
1✔
710

711
         result.test_eq("Message count", pipe.message_count(), 0);
1✔
712
         pipe.process_msg("woo");
1✔
713
         result.test_eq("Message count", pipe.message_count(), 2);
1✔
714

715
         // Test reading out of order
716
         result.test_eq("Hash 2", pipe.read_all_as_string(1), "d29v");
2✔
717
         result.test_eq("Hash 1", pipe.read_all_as_string(0), "776F6F");
2✔
718

719
         pipe.reset();
1✔
720

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

727
         pipe.append(new Botan::Threaded_Fork(filters, filter_count));
1✔
728

729
         result.test_eq("Message count before start_msg", pipe.message_count(), 2);
1✔
730

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

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

748
// NOLINTEND(*-owning-memory)
749

750
BOTAN_REGISTER_TEST("filters", "filter", Filter_Tests);
751

752
#endif
753

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