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

randombit / botan / 5134090420

31 May 2023 03:12PM UTC coverage: 91.721% (-0.3%) from 91.995%
5134090420

push

github

randombit
Merge GH #3565 Disable noisy/pointless pylint warnings

76048 of 82912 relevant lines covered (91.72%)

11755290.1 hits per line

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

97.94
/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

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

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

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

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

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

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

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

93
         return result;
1✔
94
      }
×
95

96
      static Test::Result test_data_src_sink() {
1✔
97
         Test::Result result("DataSink");
1✔
98

99
   #if defined(BOTAN_HAS_CODEC_FILTERS)
100
         std::ostringstream oss;
1✔
101

102
         Botan::Pipe pipe(new Botan::Hex_Decoder, new Botan::DataSink_Stream(oss));
1✔
103

104
         Botan::DataSource_Memory input_mem("65666768");
1✔
105
         pipe.process_msg(input_mem);
1✔
106

107
         result.test_eq("output string", oss.str(), "efgh");
3✔
108

109
         Botan::DataSource_Memory input_mem2("41414141");
1✔
110
         pipe.process_msg(input_mem2);
1✔
111

112
         result.test_eq("output string", oss.str(), "efghAAAA");
3✔
113

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

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

123
      static Test::Result test_data_src_sink_flush() {
1✔
124
         Test::Result result("DataSinkFlush");
1✔
125

126
   #if defined(BOTAN_HAS_CODEC_FILTERS) && defined(BOTAN_TARGET_OS_HAS_FILESYSTEM)
127

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

134
         std::ofstream outfile(tmp_name);
1✔
135

136
         Botan::Pipe pipe(new Botan::Hex_Decoder, new Botan::DataSink_Stream(outfile));
1✔
137

138
         Botan::DataSource_Memory input_mem("65666768");
1✔
139
         pipe.process_msg(input_mem);
1✔
140

141
         std::ifstream outfile_read(tmp_name);
1✔
142
         std::stringstream ss;
1✔
143
         ss << outfile_read.rdbuf();
1✔
144
         std::string foo = ss.str();
1✔
145

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

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

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

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

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

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

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

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

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

181
         return result;
1✔
182
      }
1✔
183

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

187
         Botan::Pipe pipe;
1✔
188

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

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

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

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

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

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

210
         pipe.start_msg();
1✔
211

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

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

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

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

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

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

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

232
         pipe.end_msg();
1✔
233

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

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

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

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

252
         return result;
2✔
253
      }
2✔
254

255
      static Test::Result test_pipe_mac() {
1✔
256
         Test::Result result("Pipe");
1✔
257

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

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

263
         mac_filter->set_iv(Botan::InitializationVector());  // ignored
1✔
264

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

269
         Botan::Pipe pipe(mac_filter, new Botan::Base64_Encoder);
1✔
270

271
         pipe.process_msg("Hi");
1✔
272
         pipe.process_msg("Bye");
1✔
273
         pipe.process_msg("Hi");
1✔
274

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

282
      static Test::Result test_pipe_hash() {
1✔
283
         Test::Result result("Pipe");
1✔
284

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

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

292
         result.test_eq("Message count", pipe.message_count(), 0);
1✔
293

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

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

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

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

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

323
         pipe.reset();
1✔
324

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

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

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

340
         // Generated with Botan 1.10
341

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

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

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

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

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

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

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

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

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

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

392
         return result;
2✔
393
      }
18✔
394

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

462
   #if defined(BOTAN_HAS_ZLIB)
463

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

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

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

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

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

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

484
         pipe.process_msg(compr);
1✔
485

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

490
         return result;
1✔
491
      }
3✔
492

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

496
   #if defined(BOTAN_HAS_BZIP2)
497

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

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

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

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

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

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

517
         pipe.process_msg(compr);
1✔
518

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

523
         return result;
1✔
524
      }
2✔
525

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

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

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

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

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

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

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

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

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

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

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

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

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

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

567
         // Now tests with line wrapping enabled
568

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

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

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

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

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

600
         return result;
2✔
601
      }
1✔
602

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

670
         return result;
2✔
671
      }
1✔
672

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

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

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

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

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

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

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

699
         return result;
1✔
700
      }
1✔
701

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

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

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

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

716
         pipe.reset();
1✔
717

718
         const size_t filter_count = 5;
719
         Botan::Filter* filters[filter_count];
720
         for(size_t i = 0; i != filter_count; ++i) {
6✔
721
            filters[i] = new Botan::Hash_Filter("SHA-256");
5✔
722
         }
723

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

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

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

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

746
BOTAN_REGISTER_TEST("filters", "filter", Filter_Tests);
747

748
#endif
749

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