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

randombit / botan / 11844561993

14 Nov 2024 07:58PM UTC coverage: 91.178% (+0.1%) from 91.072%
11844561993

Pull #4435

github

web-flow
Merge 81dcb29da into e430f157a
Pull Request #4435: Test duration values ​​are now presented in seconds with six digits of precision. Tests without time measurements have been edited.

91856 of 100744 relevant lines covered (91.18%)

9311006.71 hits per line

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

98.08
/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
class Filter_Tests final : public Test {
×
35
   public:
36
      std::vector<Test::Result> run() override {
1✔
37
         std::vector<Test::Result> results;
1✔
38

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

57
         return results;
1✔
58
      }
×
59

60
   private:
61
      static Test::Result test_secqueue() {
1✔
62
         Test::Result result("SecureQueue");
1✔
63
         result.start_timer();
1✔
64

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

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

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

75
            uint8_t b;
1✔
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
            size_t bytes_read = queue_a.read_byte(b);
1✔
79
            result.test_eq("1 byte read", bytes_read, 1);
1✔
80

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

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

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

94
         result.end_timer();
1✔
95
         return result;
1✔
96
      }
×
97

98
      static Test::Result test_data_src_sink() {
1✔
99
         Test::Result result("DataSink");
1✔
100
         result.start_timer();
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
         result.end_timer();
1✔
124
         return result;
1✔
125
      }
3✔
126

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

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

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

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

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

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

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

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

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

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

164
      static Test::Result test_pipe_io() {
1✔
165
         Test::Result result("Pipe I/O operators");
1✔
166
         result.start_timer();
1✔
167

168
   #if defined(BOTAN_HAS_CODEC_FILTERS)
169
         Botan::Pipe pipe(new Botan::Hex_Encoder);
1✔
170

171
         pipe.process_msg("ABCD");
1✔
172

173
         std::ostringstream oss;
1✔
174
         oss << pipe;
1✔
175
         result.test_eq("output string", oss.str(), "41424344");
3✔
176

177
         std::istringstream iss("AAAA");
1✔
178
         pipe.start_msg();
1✔
179
         iss >> pipe;
1✔
180
         pipe.end_msg();
1✔
181

182
         pipe.set_default_msg(1);
1✔
183
         oss << pipe;
1✔
184
         result.test_eq("output string2", oss.str(), "4142434441414141");
2✔
185
   #endif
186

187
         result.end_timer();
1✔
188
         return result;
1✔
189
      }
1✔
190

191
      static Test::Result test_pipe_errors() {
1✔
192
         Test::Result result("Pipe");
1✔
193
         result.start_timer();
1✔
194

195
         Botan::Pipe pipe;
1✔
196

197
         pipe.append(nullptr);          // ignored
1✔
198
         pipe.append_filter(nullptr);   // ignored
1✔
199
         pipe.prepend(nullptr);         // ignored
1✔
200
         pipe.prepend_filter(nullptr);  // ignored
1✔
201
         pipe.pop();                    // empty pipe, so ignored
1✔
202

203
         auto queue_filter = std::make_shared<Botan::SecureQueue>();
1✔
204

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

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

212
         pipe.append_filter(new Botan::BitBucket);  // succeeds
1✔
213
         pipe.pop();
1✔
214

215
         pipe.prepend_filter(new Botan::BitBucket);  // succeeds
1✔
216
         pipe.pop();
1✔
217

218
         pipe.start_msg();
1✔
219

220
         auto filter = std::make_unique<Botan::BitBucket>();
1✔
221

222
         // now inside a message, cannot modify pipe structure
223

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

228
         result.test_throws("pipe error", "Cannot call Pipe::prepend_filter after start_msg", [&]() {
2✔
229
            pipe.prepend_filter(filter.get());
1✔
230
         });
231

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

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

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

240
         pipe.end_msg();
1✔
241

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

246
         result.test_throws("pipe error", "Cannot call Pipe::prepend_filter after start_msg", [&]() {
2✔
247
            pipe.prepend_filter(filter.get());
1✔
248
         });
249

250
         result.test_throws("pipe error", "Pipe::read: Invalid message number 100", [&]() {
2✔
251
            uint8_t b;
1✔
252
            size_t got = pipe.read(&b, 1, 100);
1✔
253
            BOTAN_UNUSED(got);
×
254
         });
255

256
         pipe.append(nullptr);   // ignored
1✔
257
         pipe.prepend(nullptr);  // ignored
1✔
258
         pipe.pop();             // empty pipe, so ignored
1✔
259

260
         result.end_timer();
1✔
261
         return result;
2✔
262
      }
2✔
263

264
      static Test::Result test_pipe_mac() {
1✔
265
         Test::Result result("Pipe");
1✔
266
         result.start_timer();
1✔
267

268
   #if defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_HAS_HMAC) && defined(BOTAN_HAS_SHA2_32)
269
         const Botan::SymmetricKey key("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
1✔
270

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

273
         mac_filter->set_iv(Botan::InitializationVector());  // ignored
1✔
274

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

279
         Botan::Pipe pipe(mac_filter, new Botan::Base64_Encoder);
1✔
280

281
         pipe.process_msg("Hi");
1✔
282
         pipe.process_msg("Bye");
1✔
283
         pipe.process_msg("Hi");
1✔
284

285
         result.test_eq("MAC 1", pipe.read_all_as_string(0), "e7NoVbtudgU0QiCZ");
2✔
286
         result.test_eq("MAC 2", pipe.read_all_as_string(1), "LhPnfEG+0rk+Ej6y");
2✔
287
         result.test_eq("MAC 3", pipe.read_all_as_string(2), "e7NoVbtudgU0QiCZ");
2✔
288
   #endif
289

290
         result.end_timer();
1✔
291
         return result;
2✔
292
      }
2✔
293

294
      static Test::Result test_pipe_hash() {
1✔
295
         Test::Result result("Pipe");
1✔
296
         result.start_timer();
1✔
297

298
   #if defined(BOTAN_HAS_SHA2_32)
299
         // unrelated test of popping a chain
300
         Botan::Pipe pipe(new Botan::Chain(new Botan::Hash_Filter("SHA-224"), new Botan::Hash_Filter("SHA-224")));
1✔
301
         pipe.pop();
1✔
302

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

305
         result.test_eq("Message count", pipe.message_count(), 0);
1✔
306

307
         pipe.start_msg();
1✔
308
         uint8_t inb = 0x41;
1✔
309
         pipe.write(&inb, 1);
1✔
310
         pipe.write(std::vector<uint8_t>(6, 0x41));
2✔
311
         pipe.write(inb);
1✔
312
         pipe.end_msg();
1✔
313

314
         result.test_eq("Message count", pipe.message_count(), 1);
1✔
315
         result.test_eq("Message size", pipe.remaining(), 32);
1✔
316

317
         std::vector<uint8_t> out(32), last16(16);
1✔
318

319
         result.test_eq("Bytes read", pipe.get_bytes_read(0), 0);
1✔
320
         result.test_eq("More to read", pipe.end_of_data(), false);
1✔
321
         result.test_eq("Expected read count", pipe.read(&out[0], 5), 5);
1✔
322
         result.test_eq("Bytes read", pipe.get_bytes_read(0), 5);
1✔
323
         result.test_eq("Peek read", pipe.peek(last16.data(), 18, 11), 16);
1✔
324
         result.test_eq("Expected read count", pipe.read(&out[5], 17), 17);
1✔
325
         result.test_eq("Bytes read", pipe.get_bytes_read(0), 22);
1✔
326
         result.test_eq("Remaining", pipe.remaining(), 10);
1✔
327
         result.test_eq("Remaining", pipe.remaining(), 10);
1✔
328
         result.test_eq("Expected read count", pipe.read(&out[22], 12), 10);
1✔
329
         result.test_eq("Expected read count", pipe.read(&out[0], 1), 0);  // no more output
1✔
330
         result.test_eq("Bytes read", pipe.get_bytes_read(0), 32);
1✔
331
         result.test_eq("No more to read", pipe.end_of_data(), true);
1✔
332

333
         result.test_eq("Expected output", out, "C34AB6ABB7B2BB595BC25C3B388C872FD1D575819A8F55CC689510285E212385");
1✔
334
         result.test_eq("Expected last16", last16, "D1D575819A8F55CC689510285E212385");
1✔
335

336
         pipe.reset();
1✔
337

338
      #if defined(BOTAN_HAS_CRC32)
339
         pipe.prepend(new Botan::Hash_Filter("CRC32"));
1✔
340
         pipe.append(new Botan::Hash_Filter("CRC32"));
1✔
341
         pipe.process_msg(std::vector<uint8_t>(1024, 0));
1✔
342
         result.test_eq("Expected CRC32d", pipe.read_all(1), "99841F60");
2✔
343
      #endif
344
   #endif
345
         result.end_timer();
1✔
346
         return result;
2✔
347
      }
2✔
348

349
      static Test::Result test_pipe_cfb() {
1✔
350
         Test::Result result("Pipe CFB");
1✔
351
         result.start_timer();
1✔
352

353
   #if defined(BOTAN_HAS_BLOWFISH) && defined(BOTAN_HAS_MODE_CFB) && defined(BOTAN_HAS_CODEC_FILTERS)
354

355
         // Generated with Botan 1.10
356

357
         const Botan::InitializationVector iv("AABBCCDDEEFF0123");
1✔
358
         const Botan::SymmetricKey key("AABBCCDDEEFF0123");
1✔
359

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

362
         const std::string cfb_expected[] = {
1✔
363
            "A4",
364
            "BEA4",
365
            "06AD98",
366
            "E4AFC5AC",
367
            "A9B531559C",
368
            "38B60DA66445",
369
            "194F5E93199839",
370
            "093B6381D2E5D806",
371
            "B44FA624226EECF027",
372
            "80B8DC3332A835AC11A8",
373
            "2C0E910A1E5C38344CC5BB",
374
            "3CB6180AE2E189342F681023",
375
            "DE0F4B10C7D9CADDB5A9078199",
376
            "FAE18B0ED873F234CCD6E1555B2D",
377
            "7195FFE735B0A95065BA244C77A11F",
378
         };
16✔
379

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

383
         Botan::Keyed_Filter* cfb_dec = Botan::get_cipher("Blowfish/CFB", key, Botan::Cipher_Dir::Decryption);
1✔
384
         cfb_dec->set_iv(iv);
1✔
385
         Botan::Pipe dec_pipe(new Botan::Hex_Decoder, cfb_dec, new Botan::Hex_Encoder);
1✔
386

387
         for(size_t i = 1; i != sizeof(msg_bits); ++i) {
15✔
388
            enc_pipe.start_msg();
14✔
389
            enc_pipe.write(msg_bits, i);
14✔
390
            enc_pipe.end_msg();
14✔
391

392
            dec_pipe.process_msg(cfb_expected[i - 1]);
14✔
393
         }
394

395
         result.test_eq("enc pipe msg count", enc_pipe.message_count(), sizeof(msg_bits) - 1);
1✔
396
         result.test_eq("dec pipe msg count", dec_pipe.message_count(), sizeof(msg_bits) - 1);
1✔
397

398
         for(size_t i = 0; i != enc_pipe.message_count(); ++i) {
15✔
399
            result.test_eq("encrypt", enc_pipe.read_all_as_string(i), cfb_expected[i]);
28✔
400
         }
401

402
         for(size_t i = 0; i != dec_pipe.message_count(); ++i) {
15✔
403
            result.test_eq("decrypt", dec_pipe.read_all_as_string(i), Botan::hex_encode(msg_bits, i + 1));
28✔
404
         }
405
   #endif
406

407
         result.end_timer();
1✔
408
         return result;
2✔
409
      }
18✔
410

411
      static Test::Result test_pipe_cbc() {
1✔
412
         Test::Result result("Pipe CBC");
1✔
413
         result.start_timer();
1✔
414

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

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

421
         result.test_eq("Cipher filter nonce size", cipher->valid_iv_length(16), true);
1✔
422
         result.test_eq("Cipher filter nonce size", cipher->valid_iv_length(17), false);
1✔
423

424
         result.test_eq("Cipher key length max", cipher->key_spec().maximum_keylength(), 16);
1✔
425
         result.test_eq("Cipher key length min", cipher->key_spec().minimum_keylength(), 16);
1✔
426

427
         // takes ownership of cipher
428
         Botan::Pipe pipe(cipher);
1✔
429

430
         cipher->set_key(Botan::SymmetricKey("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
1✔
431
         cipher->set_iv(Botan::InitializationVector("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
1✔
432

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

435
         result.test_eq("Message count", pipe.message_count(), 1);
1✔
436
         result.test_eq("Bytes read", pipe.get_bytes_read(), 0);
1✔
437
         auto ciphertext = pipe.read_all();
1✔
438
         result.test_eq("Bytes read after", pipe.get_bytes_read(), ciphertext.size());
1✔
439

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

442
         pipe.process_msg("IV carryover");
1✔
443
         auto ciphertext2 = pipe.read_all(1);
1✔
444
         pipe.process_msg("IV carryover2");
1✔
445
         auto ciphertext3 = pipe.read_all(2);
1✔
446

447
         // These values tested against PyCrypto
448
         result.test_eq("Ciphertext2", ciphertext2, "AA8D682958A4A044735DAC502B274DB2");
1✔
449
         result.test_eq("Ciphertext3", ciphertext3, "1241B9976F73051BCF809525D6E86C25");
1✔
450

451
         Botan::Cipher_Mode_Filter* dec_cipher = new Botan::Cipher_Mode_Filter(
1✔
452
            Botan::Cipher_Mode::create("AES-128/CBC/PKCS7", Botan::Cipher_Dir::Decryption));
2✔
453
         pipe.append(dec_cipher);
1✔
454
         dec_cipher->set_key(Botan::SymmetricKey("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"));
1✔
455
         dec_cipher->set_iv(Botan::InitializationVector("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB"));
1✔
456

457
         // reset the IV on the encryption filter
458
         cipher->set_iv(Botan::InitializationVector("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB"));
1✔
459

460
         const std::vector<uint8_t> zeros_in(1024);
1✔
461
         Botan::DataSource_Memory src(zeros_in);
1✔
462
         pipe.start_msg();
1✔
463
         pipe.write(src);
1✔
464
         pipe.end_msg();
1✔
465

466
         pipe.set_default_msg(3);
1✔
467
         result.test_eq("Bytes read", pipe.get_bytes_read(), 0);
1✔
468
         Botan::secure_vector<uint8_t> zeros_out = pipe.read_all();
1✔
469
         result.test_eq("Bytes read", pipe.get_bytes_read(), zeros_out.size());
1✔
470

471
         result.test_eq("Cipher roundtrip", zeros_in, zeros_out);
1✔
472
   #endif
473

474
         result.end_timer();
1✔
475
         return result;
2✔
476
      }
6✔
477

478
      static Test::Result test_pipe_compress() {
1✔
479
         Test::Result result("Pipe compress zlib");
1✔
480
         result.start_timer();
1✔
481

482
   #if defined(BOTAN_HAS_ZLIB)
483

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

486
         result.test_eq("Compressor filter name", comp_f->name(), "Zlib_Compression");
2✔
487
         Botan::Pipe pipe(comp_f.release());
1✔
488

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

491
         pipe.start_msg();
1✔
492
         pipe.write(input_str);
1✔
493
         pipe.end_msg();
1✔
494

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

499
         auto decomp_f = std::make_unique<Botan::Decompression_Filter>("zlib");
1✔
500
         result.test_eq("Decompressor name", decomp_f->name(), "Zlib_Decompression");
2✔
501
         pipe.append(decomp_f.release());
1✔
502
         pipe.pop();  // remove compressor
1✔
503

504
         pipe.process_msg(compr);
1✔
505

506
         std::string decomp = pipe.read_all_as_string(1);
1✔
507
         result.test_eq("Decompressed ok", decomp, input_str);
1✔
508
   #endif
509

510
         result.end_timer();
1✔
511
         return result;
2✔
512
      }
2✔
513

514
      static Test::Result test_pipe_compress_bzip2() {
1✔
515
         Test::Result result("Pipe compress bzip2");
1✔
516
         result.start_timer();
1✔
517

518
   #if defined(BOTAN_HAS_BZIP2)
519

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

522
         result.test_eq("Compressor filter name", comp_f->name(), "Bzip2_Compression");
2✔
523
         Botan::Pipe pipe(comp_f.release());
1✔
524

525
         const std::string input_str = "foo\n";
1✔
526

527
         pipe.start_msg();
1✔
528
         pipe.write(input_str);
1✔
529
         pipe.end_msg();
1✔
530

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

534
         auto decomp_f = std::make_unique<Botan::Decompression_Filter>("bzip2");
1✔
535
         result.test_eq("Decompressor name", decomp_f->name(), "Bzip2_Decompression");
2✔
536
         pipe.append(decomp_f.release());
1✔
537
         pipe.pop();  // remove compressor
1✔
538

539
         pipe.process_msg(compr);
1✔
540

541
         std::string decomp = pipe.read_all_as_string(1);
1✔
542
         result.test_eq("Decompressed ok", decomp, input_str);
1✔
543
   #endif
544

545
         result.end_timer();
1✔
546
         return result;
2✔
547
      }
2✔
548

549
      static Test::Result test_pipe_codec() {
1✔
550
         Test::Result result("Pipe");
1✔
551
         result.start_timer();
1✔
552

553
   #if defined(BOTAN_HAS_CODEC_FILTERS)
554
         Botan::Pipe pipe(new Botan::Base64_Encoder);
1✔
555

556
         result.test_eq("Message count", pipe.message_count(), 0);
1✔
557

558
         pipe.process_msg("ABCDX");
1✔
559

560
         result.test_eq("Message count", pipe.message_count(), 1);
1✔
561
         result.test_eq("Message size", pipe.remaining(), 8);
1✔
562

563
         std::string output = pipe.read_all_as_string(0);
1✔
564
         result.test_eq("Message size", pipe.remaining(0), 0);
1✔
565
         result.test_eq("Output round tripped", output, "QUJDRFg=");
2✔
566

567
         pipe.append(new Botan::Base64_Decoder);
1✔
568
         pipe.process_msg("FOOBAZ");
1✔
569

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

572
         pipe.pop();
1✔
573
         pipe.pop();
1✔
574

575
         // Pipe is empty of filters, should still pass through
576
         pipe.process_msg("surprise plaintext");
1✔
577

578
         pipe.set_default_msg(2);
1✔
579
         result.test_eq("Message 2", pipe.read_all_as_string(), "surprise plaintext");
2✔
580

581
         pipe.append(new Botan::Hex_Decoder);
1✔
582

583
         pipe.process_msg("F331F00D");
1✔
584
         Botan::secure_vector<uint8_t> bin = pipe.read_all(3);
1✔
585
         result.test_eq("hex decoded", bin, "F331F00D");
1✔
586

587
         pipe.append(new Botan::Hex_Encoder);
1✔
588
         pipe.process_msg("F331F00D");
1✔
589
         result.test_eq("hex roundtrip", pipe.read_all_as_string(4), "F331F00D");
2✔
590

591
         // Now tests with line wrapping enabled
592

593
         pipe.reset();
1✔
594
         pipe.append(new Botan::Hex_Decoder);
1✔
595
         pipe.append(new Botan::Base64_Encoder(/*break_lines=*/true,
1✔
596
                                               /*line_length=*/4,
597
                                               /*trailing_newline=*/true));
1✔
598

599
         pipe.process_msg("6dab1eeb8a2eb69bad");
1✔
600
         result.test_eq(
3✔
601
            "base64 with linebreaks and trailing newline", pipe.read_all_as_string(5), "base\n64ou\ntput\n\n");
2✔
602

603
         pipe.reset();
1✔
604
         pipe.append(new Botan::Hex_Decoder);
1✔
605
         pipe.append(new Botan::Base64_Encoder(true, 5, false));
1✔
606
         pipe.process_msg("6dab1eeb8a2eb69bad");
1✔
607
         result.test_eq("base64 with linebreaks", pipe.read_all_as_string(6), "base6\n4outp\nut\n");
2✔
608

609
         pipe.reset();
1✔
610
         pipe.append(new Botan::Hex_Encoder(true, 13, Botan::Hex_Encoder::Uppercase));
1✔
611
         pipe.process_msg("hex encoding this string");
1✔
612
         result.test_eq("hex uppercase with linebreaks",
3✔
613
                        pipe.read_all_as_string(7),
2✔
614
                        "68657820656E6\n36F64696E6720\n7468697320737\n472696E67\n");
615

616
         pipe.reset();
1✔
617
         pipe.append(new Botan::Hex_Encoder(true, 16, Botan::Hex_Encoder::Lowercase));
1✔
618
         pipe.process_msg("hex encoding this string");
1✔
619
         result.test_eq("hex lowercase with linebreaks",
3✔
620
                        pipe.read_all_as_string(8),
2✔
621
                        "68657820656e636f\n64696e6720746869\n7320737472696e67\n");
622
   #endif
623

624
         result.end_timer();
1✔
625
         return result;
2✔
626
      }
1✔
627

628
      static Test::Result test_pipe_stream() {
1✔
629
         Test::Result result("Pipe CTR");
1✔
630
         result.start_timer();
1✔
631

632
   #if defined(BOTAN_HAS_CTR_BE) && defined(BOTAN_HAS_AES)
633
         Botan::Keyed_Filter* aes = nullptr;
1✔
634
         const Botan::SymmetricKey some_other_key("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE");
1✔
635
         const Botan::SymmetricKey key("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
1✔
636
         const Botan::InitializationVector iv("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
1✔
637
         Botan::Pipe pipe(aes = new Botan::StreamCipher_Filter("CTR-BE(AES-128)", some_other_key));
1✔
638

639
         aes->set_key(key);
1✔
640
         aes->set_iv(iv);
1✔
641

642
         pipe.process_msg("ABCDEF");
1✔
643

644
         result.test_eq("Message count", pipe.message_count(), 1);
1✔
645
         result.test_eq("Ciphertext", pipe.read_all(), "FDFD6238F7C6");
2✔
646

647
         pipe.process_msg("ABCDEF");
1✔
648
         result.test_eq("Ciphertext", pipe.read_all(1), "8E72F1153514");
2✔
649
   #endif
650

651
         result.end_timer();
1✔
652
         return result;
2✔
653
      }
4✔
654

655
      static Test::Result test_fork() {
1✔
656
         Test::Result result("Filter Fork");
1✔
657
         result.start_timer();
1✔
658

659
   #if defined(BOTAN_HAS_SHA2_32) && defined(BOTAN_HAS_SHA2_64)
660
         Botan::Pipe pipe(new Botan::Fork(new Botan::Hash_Filter("SHA-256"), new Botan::Hash_Filter("SHA-512-256")));
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
         // Test reading out of order
667
         result.test_eq("Hash 2", pipe.read_all(1), "610480FFA82F24F6926544B976FE387878E3D973C03DFD591C2E9896EFB903E0");
2✔
668
         result.test_eq("Hash 1", pipe.read_all(0), "C00862D1C6C1CF7C1B49388306E7B3C1BB79D8D6EC978B41035B556DBB3797DF");
2✔
669
   #endif
670

671
         result.end_timer();
1✔
672
         return result;
1✔
673
      }
1✔
674

675
      static Test::Result test_chain() {
1✔
676
         Test::Result result("Filter Chain");
1✔
677
         result.start_timer();
1✔
678

679
   #if defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_HAS_SHA2_32) && defined(BOTAN_HAS_SHA2_64)
680

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

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

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

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

690
         result.test_eq("Fork has a name", fork->name(), "Fork");
2✔
691
         Botan::Pipe pipe(fork.release());
1✔
692

693
         result.test_eq("Message count", pipe.message_count(), 0);
1✔
694
         pipe.process_msg("OMG");
1✔
695
         result.test_eq("Message count", pipe.message_count(), 2);
1✔
696

697
         result.test_eq(
3✔
698
            "Hash 1", pipe.read_all_as_string(0), "C00862D1C6C1CF7C1B49388306E7B3C1BB79D8D6EC978B41035B556DBB3797DF");
2✔
699
         result.test_eq("Hash 2", pipe.read_all_as_string(1), "610480FFA82F24F6926544B976FE387878E3D9");
2✔
700
   #endif
701

702
         result.end_timer();
1✔
703
         return result;
1✔
704
      }
1✔
705

706
      static Test::Result test_pipe_fd_io() {
1✔
707
         Test::Result result("Pipe file descriptor IO");
1✔
708
         result.start_timer();
1✔
709

710
   #if defined(BOTAN_HAS_PIPE_UNIXFD_IO) && defined(BOTAN_HAS_CODEC_FILTERS)
711
         int fd[2];
1✔
712
         if(::pipe(fd) != 0) {
1✔
713
            return result;  // pipe unavailable?
714
         }
715

716
         Botan::Pipe hex_enc(new Botan::Hex_Encoder);
1✔
717
         Botan::Pipe hex_dec(new Botan::Hex_Decoder);
1✔
718

719
         hex_enc.process_msg("hi chappy");
1✔
720
         fd[1] << hex_enc;
1✔
721
         ::close(fd[1]);
1✔
722

723
         hex_dec.start_msg();
1✔
724
         fd[0] >> hex_dec;
1✔
725
         hex_dec.end_msg();
1✔
726
         ::close(fd[0]);
1✔
727

728
         std::string dec = hex_dec.read_all_as_string();
1✔
729

730
         result.test_eq("IO through Unix pipe works", dec, "hi chappy");
2✔
731
   #endif
732

733
         result.end_timer();
1✔
734
         return result;
1✔
735
      }
1✔
736

737
      static Test::Result test_threaded_fork() {
1✔
738
         Test::Result result("Threaded_Fork");
1✔
739
         result.start_timer();
1✔
740

741
   #if defined(BOTAN_HAS_THREAD_UTILS) && defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_HAS_SHA2_32)
742
         Botan::Pipe pipe(new Botan::Threaded_Fork(new Botan::Hex_Encoder, new Botan::Base64_Encoder));
1✔
743

744
         result.test_eq("Message count", pipe.message_count(), 0);
1✔
745
         pipe.process_msg("woo");
1✔
746
         result.test_eq("Message count", pipe.message_count(), 2);
1✔
747

748
         // Test reading out of order
749
         result.test_eq("Hash 2", pipe.read_all_as_string(1), "d29v");
2✔
750
         result.test_eq("Hash 1", pipe.read_all_as_string(0), "776F6F");
2✔
751

752
         pipe.reset();
1✔
753

754
         const size_t filter_count = 5;
755
         Botan::Filter* filters[filter_count];
756
         for(size_t i = 0; i != filter_count; ++i) {
6✔
757
            filters[i] = new Botan::Hash_Filter("SHA-256");
5✔
758
         }
759

760
         pipe.append(new Botan::Threaded_Fork(filters, filter_count));
1✔
761

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

764
         pipe.start_msg();
1✔
765
         for(size_t i = 0; i != 919; ++i) {
920✔
766
            std::vector<uint8_t> input(i + 5, static_cast<uint8_t>(i));
919✔
767
            pipe.write(input);
919✔
768
         }
919✔
769
         pipe.end_msg();
1✔
770

771
         result.test_eq("Message count after end_msg", pipe.message_count(), 2 + filter_count);
1✔
772
         for(size_t i = 0; i != filter_count; ++i) {
6✔
773
            result.test_eq(
10✔
774
               "Output", pipe.read_all(2 + i), "327AD8055223F5926693D8BEA40F7B35BDEEB535647DFB93F464E40EA01939A9");
10✔
775
         }
776
   #endif
777
         result.end_timer();
1✔
778
         return result;
1✔
779
      }
1✔
780
};
781

782
BOTAN_REGISTER_TEST("filters", "filter", Filter_Tests);
783

784
#endif
785

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

© 2025 Coveralls, Inc