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

randombit / botan / 20712771074

05 Jan 2026 10:39AM UTC coverage: 90.422% (-0.007%) from 90.429%
20712771074

Pull #5209

github

web-flow
Merge 8ef1d9177 into cf0e68e34
Pull Request #5209: Improve support for running tests on Emscripten

101646 of 112413 relevant lines covered (90.42%)

12824143.69 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/pipe.h>
18
   #include <botan/internal/secqueue.h>
19
   #include <botan/internal/target_info.h>
20
#endif
21

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

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

31
namespace Botan_Tests {
32

33
#if defined(BOTAN_HAS_FILTERS)
34

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

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

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

60
         return results;
1✔
61
      }
×
62

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

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

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

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

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

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

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

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

96
         return result;
1✔
97
      }
×
98

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

183
         return result;
1✔
184
      }
1✔
185

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

189
         Botan::Pipe pipe;
1✔
190

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

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

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

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

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

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

212
         pipe.start_msg();
1✔
213

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

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

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

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

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

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

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

234
         pipe.end_msg();
1✔
235

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

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

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

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

254
         return result;
2✔
255
      }
2✔
256

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

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

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

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

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

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

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

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

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

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

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

294
         result.test_eq("Message count", pipe.message_count(), 0);
1✔
295

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

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

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

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

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

326
         pipe.reset();
1✔
327

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

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

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

343
         // Generated with Botan 1.10
344

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

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

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

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

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

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

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

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

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

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

395
         return result;
2✔
396
      }
18✔
397

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

465
   #if defined(BOTAN_HAS_ZLIB)
466

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

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

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

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

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

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

487
         pipe.process_msg(compr);
1✔
488

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

493
         return result;
2✔
494
      }
2✔
495

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

499
   #if defined(BOTAN_HAS_BZIP2)
500

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

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

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

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

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

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

520
         pipe.process_msg(compr);
1✔
521

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

526
         return result;
2✔
527
      }
2✔
528

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

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

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

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

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

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

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

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

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

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

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

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

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

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

570
         // Now tests with line wrapping enabled
571

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

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

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

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

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

603
         return result;
2✔
604
      }
1✔
605

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

674
         return result;
1✔
675
      }
1✔
676

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

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

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

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

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

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

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

703
         return result;
1✔
704
      }
1✔
705

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

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

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

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

720
         pipe.reset();
1✔
721

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

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

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

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

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

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

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

753
#endif
754

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