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

randombit / botan / 21768358452

06 Feb 2026 10:35PM UTC coverage: 90.064% (-0.003%) from 90.067%
21768358452

Pull #5289

github

web-flow
Merge f589db195 into 8ea0ca252
Pull Request #5289: Further misc header reductions, forward declarations, etc

102238 of 113517 relevant lines covered (90.06%)

11357432.36 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
#define BOTAN_NO_DEPRECATED_WARNINGS
11

12
#include "tests.h"
13

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

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

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

32
namespace Botan_Tests {
33

34
#if defined(BOTAN_HAS_FILTERS)
35

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

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

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

61
         return results;
1✔
62
      }
×
63

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

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

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

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

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

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

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

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

97
         return result;
1✔
98
      }
×
99

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

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

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

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

111
         result.test_eq("output string", oss.str(), "efgh");
3✔
112

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

116
         result.test_eq("output string", oss.str(), "efghAAAA");
3✔
117

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

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

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

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

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

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

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

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

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

149
         result.test_eq("output string", ss.str(), "efgh");
3✔
150

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

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

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

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

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

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

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

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

184
         return result;
1✔
185
      }
1✔
186

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

190
         Botan::Pipe pipe;
1✔
191

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

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

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

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

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

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

213
         pipe.start_msg();
1✔
214

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

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

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

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

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

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

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

235
         pipe.end_msg();
1✔
236

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

466
   #if defined(BOTAN_HAS_ZLIB)
467

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

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

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

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

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

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

488
         pipe.process_msg(compr);
1✔
489

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

494
         return result;
2✔
495
      }
2✔
496

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

500
   #if defined(BOTAN_HAS_BZIP2)
501

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

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

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

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

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

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

521
         pipe.process_msg(compr);
1✔
522

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

527
         return result;
2✔
528
      }
2✔
529

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

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

536
         result.test_eq("Message count", pipe.message_count(), 0);
1✔
537

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

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

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

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

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

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

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

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

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

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

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

571
         // Now tests with line wrapping enabled
572

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

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

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

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

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

604
         return result;
2✔
605
      }
1✔
606

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

675
         return result;
1✔
676
      }
1✔
677

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

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

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

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

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

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

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

704
         return result;
1✔
705
      }
1✔
706

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

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

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

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

721
         pipe.reset();
1✔
722

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

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

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

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

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

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

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

754
#endif
755

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