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

randombit / botan / 16248620128

13 Jul 2025 11:27AM UTC coverage: 90.565% (-0.01%) from 90.575%
16248620128

push

github

web-flow
Merge pull request #4983 from randombit/jack/clang-tidy-cppcoreguidelines-owning-memory

Enable and fix clang-tidy warning cppcoreguidelines-owning-memory

99026 of 109342 relevant lines covered (90.57%)

12444574.04 hits per line

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

97.93
/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/pipe.h>
18
   #include <botan/internal/secqueue.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 {
×
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
            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
            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
            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
         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), last16(16);
1✔
306

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

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

324
         pipe.reset();
1✔
325

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

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

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

341
         // Generated with Botan 1.10
342

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

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

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

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

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

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

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

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

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

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

393
         return result;
2✔
394
      }
18✔
395

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

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

403
         result.test_eq("Cipher filter name", cipher->name(), "AES-128/CBC/PKCS7");
2✔
404

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

463
   #if defined(BOTAN_HAS_ZLIB)
464

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

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

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

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

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

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

485
         pipe.process_msg(compr);
1✔
486

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

491
         return result;
2✔
492
      }
2✔
493

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

497
   #if defined(BOTAN_HAS_BZIP2)
498

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

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

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

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

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

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

518
         pipe.process_msg(compr);
1✔
519

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

524
         return result;
2✔
525
      }
2✔
526

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

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

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

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

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

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

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

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

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

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

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

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

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

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

568
         // Now tests with line wrapping enabled
569

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

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

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

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

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

601
         return result;
2✔
602
      }
1✔
603

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

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

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

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

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

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

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

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

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

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

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

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

650
         Botan::Filter* filters[2] = {new Botan::Hash_Filter("SHA-256"), new Botan::Hex_Encoder};
1✔
651

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

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

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

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

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

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

671
         return result;
1✔
672
      }
1✔
673

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

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

683
         Botan::Pipe hex_enc(new Botan::Hex_Encoder);
1✔
684
         Botan::Pipe hex_dec(new Botan::Hex_Decoder);
1✔
685

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

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

695
         std::string dec = hex_dec.read_all_as_string();
1✔
696

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

700
         return result;
1✔
701
      }
1✔
702

703
      static Test::Result test_threaded_fork() {
1✔
704
         Test::Result result("Threaded_Fork");
1✔
705

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

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

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

717
         pipe.reset();
1✔
718

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

725
         pipe.append(new Botan::Threaded_Fork(filters, filter_count));
1✔
726

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

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

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

746
// NOLINTEND(*-owning-memory)
747

748
BOTAN_REGISTER_TEST("filters", "filter", Filter_Tests);
749

750
#endif
751

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