|
19 | 19 | #define BOOST_TEST_MODULE "C++ Unit Tests for libcdoc" |
20 | 20 |
|
21 | 21 | #include <boost/test/unit_test.hpp> |
| 22 | +#include <codecvt> |
22 | 23 | #include <filesystem> |
23 | 24 | #include <fstream> |
24 | 25 | #include <map> |
25 | 26 | #include <CDocCipher.h> |
| 27 | +#include <CryptoBackend.h> |
26 | 28 | #include <Recipient.h> |
27 | 29 | #include <Utils.h> |
28 | 30 | #include <cdoc/Crypto.h> |
@@ -75,14 +77,24 @@ class FixtureBase |
75 | 77 | public: |
76 | 78 | FixtureBase() |
77 | 79 | { |
78 | | - // Get path to test data, provided via argument to the unit tests application |
79 | | - if (utf::framework::master_test_suite().argc <= 1) |
80 | | - { |
81 | | - testDataPath = DATA_DIR; |
82 | | - } |
83 | | - else |
84 | | - { |
85 | | - testDataPath = utf::framework::master_test_suite().argv[1]; |
| 80 | + int argc = utf::framework::master_test_suite().argc; |
| 81 | + for (int i = 0; i < argc; i++) { |
| 82 | + std::string_view arg = utf::framework::master_test_suite().argv[i]; |
| 83 | + if (arg == "--data-path") { |
| 84 | + if (i >= argc) { |
| 85 | + std::cerr << "Missing data path value" << std::endl; |
| 86 | + ::exit(1); |
| 87 | + } |
| 88 | + i += 1; |
| 89 | + testDataPath = utf::framework::master_test_suite().argv[i]; |
| 90 | + } else if (arg == "--max-filesize") { |
| 91 | + if (i >= argc) { |
| 92 | + std::cerr << "Missing max filesize value" << std::endl; |
| 93 | + ::exit(1); |
| 94 | + } |
| 95 | + i += 1; |
| 96 | + max_filesize = std::stoull(utf::framework::master_test_suite().argv[i]); |
| 97 | + } |
86 | 98 | } |
87 | 99 | } |
88 | 100 |
|
@@ -122,9 +134,10 @@ class FixtureBase |
122 | 134 | } |
123 | 135 | } |
124 | 136 |
|
125 | | - fs::path testDataPath; |
| 137 | + fs::path testDataPath = DATA_DIR; |
126 | 138 | fs::path sourceFilePath; |
127 | 139 | fs::path targetFilePath; |
| 140 | + size_t max_filesize = 100000000; |
128 | 141 | }; |
129 | 142 |
|
130 | 143 | /** |
@@ -215,6 +228,175 @@ class DecryptFixture : public FixtureBase |
215 | 228 | } |
216 | 229 | }; |
217 | 230 |
|
| 231 | +struct PipeSource : public libcdoc::DataSource { |
| 232 | + PipeSource(std::vector<uint8_t>& data, bool& eof) : _data(data), _eof(eof) {} |
| 233 | + |
| 234 | + libcdoc::result_t read(uint8_t *dst, size_t size) override { |
| 235 | + size = std::min<size_t>(size, _data.size()); |
| 236 | + std::copy(_data.cbegin(), _data.cbegin() + size, dst); |
| 237 | + if (_buf.size() < 1024) { |
| 238 | + size_t newbufsize = _buf.size() + size; |
| 239 | + if (newbufsize > 1024) newbufsize = 1024; |
| 240 | + size_t tocopy = newbufsize - _buf.size(); |
| 241 | + _buf.insert(_buf.end(), _data.begin(), _data.begin() + tocopy); |
| 242 | + } |
| 243 | + _data.erase(_data.cbegin(), _data.cbegin() + size); |
| 244 | + return size; |
| 245 | + } |
| 246 | + |
| 247 | + libcdoc::result_t seek(size_t pos) override { |
| 248 | + if (pos <= _buf.size()) { |
| 249 | + _data.insert(_data.begin(), _buf.begin() + pos, _buf.end()); |
| 250 | + _buf.erase(_buf.begin() + pos, _buf.end()); |
| 251 | + return libcdoc::OK; |
| 252 | + } |
| 253 | + return libcdoc::NOT_IMPLEMENTED; |
| 254 | + } |
| 255 | + bool isError() override { return false; } |
| 256 | + bool isEof() override { return _eof; } |
| 257 | +protected: |
| 258 | + std::vector<uint8_t>& _data; |
| 259 | + bool& _eof; |
| 260 | + std::vector<uint8_t> _buf; |
| 261 | +}; |
| 262 | + |
| 263 | +struct PipeConsumer : public libcdoc::DataConsumer { |
| 264 | + PipeConsumer(std::vector<uint8_t>& data, bool& eof) : _data(data), _eof(eof) { _eof = false; } |
| 265 | + libcdoc::result_t write(const uint8_t *src, size_t size) override final { |
| 266 | + _data.insert(_data.end(), src, src + size); |
| 267 | + return size; |
| 268 | + } |
| 269 | + libcdoc::result_t close() override final { _eof = true; return libcdoc::OK; } |
| 270 | + virtual bool isError() override final { return false; } |
| 271 | +protected: |
| 272 | + std::vector<uint8_t>& _data; |
| 273 | + bool& _eof; |
| 274 | +}; |
| 275 | + |
| 276 | +struct PipeCrypto : public libcdoc::CryptoBackend { |
| 277 | + PipeCrypto(std::string pwd) : _secret(pwd.cbegin(), pwd.cend()) {} |
| 278 | + |
| 279 | + libcdoc::result_t getSecret(std::vector<uint8_t>& dst, unsigned int idx) { |
| 280 | + dst = _secret; |
| 281 | + return libcdoc::OK; |
| 282 | + }; |
| 283 | + |
| 284 | + std::vector<uint8_t> _secret; |
| 285 | +}; |
| 286 | + |
| 287 | +struct PipeWriter { |
| 288 | + static constexpr size_t BUFSIZE = 1024 * 1024; |
| 289 | + |
| 290 | + PipeWriter(libcdoc::CDocWriter *writer, const std::vector<libcdoc::FileInfo>& files) : _writer(writer), _files(files), current(-1), cpos(0) {} |
| 291 | + |
| 292 | + uint8_t getChar(int filenum, size_t pos) { |
| 293 | + uint64_t x = pos + ((uint64_t) filenum << 40); |
| 294 | + x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL; |
| 295 | + x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL; |
| 296 | + x = x ^ (x >> 31); |
| 297 | + return (uint8_t) (x & 0xff); |
| 298 | + } |
| 299 | + |
| 300 | + libcdoc::result_t writeMore() { |
| 301 | + if (current >= (int) _files.size()) return libcdoc::WORKFLOW_ERROR; |
| 302 | + |
| 303 | + if ((current < 0) || (cpos >= _files[current].size)) { |
| 304 | + // Start new file |
| 305 | + current += 1; |
| 306 | + cpos = 0; |
| 307 | + if (current >= (int) _files.size()) { |
| 308 | + return _writer->finishEncryption(); |
| 309 | + } |
| 310 | + return _writer->addFile(_files[current].name, _files[current].size); |
| 311 | + } |
| 312 | + size_t towrite = _files[current].size - cpos; |
| 313 | + if (towrite > BUFSIZE) towrite = BUFSIZE; |
| 314 | + uint8_t buf[BUFSIZE]; |
| 315 | + for (int i = 0; i < towrite; i++) buf[i] = getChar(current, cpos + i); |
| 316 | + cpos += towrite; |
| 317 | + return _writer->writeData(buf, towrite); |
| 318 | + } |
| 319 | + |
| 320 | + bool isEof() { |
| 321 | + return current >= (int) _files.size(); |
| 322 | + } |
| 323 | + |
| 324 | + int current = 0; |
| 325 | + size_t cpos = 0; |
| 326 | + |
| 327 | + libcdoc::CDocWriter *_writer; |
| 328 | + const std::vector<libcdoc::FileInfo>& _files; |
| 329 | +}; |
| 330 | + |
| 331 | +BOOST_AUTO_TEST_SUITE(LargeFiles) |
| 332 | + |
| 333 | +BOOST_FIXTURE_TEST_CASE_WITH_DECOR(EncryptWithPasswordAndLabel, FixtureBase, * utf::description("Testing weird and large files")) |
| 334 | +{ |
| 335 | + std::vector<uint8_t> data; |
| 336 | + bool eof = false; |
| 337 | + PipeConsumer pipec(data, eof); |
| 338 | + PipeSource pipes(data, eof); |
| 339 | + PipeCrypto pcrypto("password"); |
| 340 | + |
| 341 | + // Create writer |
| 342 | + libcdoc::CDocWriter *writer = libcdoc::CDocWriter::createWriter(2, &pipec, false, nullptr, &pcrypto, nullptr); |
| 343 | + BOOST_TEST(writer != nullptr); |
| 344 | + libcdoc::Recipient rcpt = libcdoc::Recipient::makeSymmetric("test", 65536); |
| 345 | + BOOST_TEST(writer->addRecipient(rcpt) == libcdoc::OK); |
| 346 | + BOOST_TEST(writer->beginEncryption() == libcdoc::OK); |
| 347 | + |
| 348 | + std::srand(1); |
| 349 | + std::vector<libcdoc::FileInfo> files; |
| 350 | + for (size_t i = max_filesize; i != 0; i = i / 1000) { |
| 351 | + size_t len = std::rand() % 1000; |
| 352 | + std::u16string u16(len, ' '); |
| 353 | + for (int i = 0; i < len; i++) u16[i] = std::rand() % 10000 + 32; |
| 354 | + std::string u8 = std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t>{}.to_bytes(u16); |
| 355 | + files.emplace_back(u8, i); |
| 356 | + files.emplace_back(u8, 0); |
| 357 | + } |
| 358 | + |
| 359 | + PipeWriter wrt(writer, files); |
| 360 | + |
| 361 | + // Create reader |
| 362 | + libcdoc::CDocReader *reader = libcdoc::CDocReader::createReader(&pipes, false, nullptr, &pcrypto, nullptr); |
| 363 | + BOOST_TEST(reader != nullptr); |
| 364 | + |
| 365 | + // Fill buffer |
| 366 | + while((data.size() < 2 * wrt.BUFSIZE) && !wrt.isEof()) { |
| 367 | + BOOST_TEST(wrt.writeMore() == libcdoc::OK); |
| 368 | + } |
| 369 | + std::vector<uint8_t> fmk; |
| 370 | + BOOST_TEST(reader->getFMK(fmk, 0) == libcdoc::OK); |
| 371 | + BOOST_TEST(reader->beginDecryption(fmk) == libcdoc::OK); |
| 372 | + libcdoc::FileInfo fi; |
| 373 | + for (int cfile = 0; cfile < files.size(); cfile++) { |
| 374 | + // Fill buffer |
| 375 | + while((data.size() < 2 * wrt.BUFSIZE) && !wrt.isEof()) { |
| 376 | + BOOST_TEST(wrt.writeMore() == libcdoc::OK); |
| 377 | + } |
| 378 | + // Get file |
| 379 | + BOOST_TEST(reader->nextFile(fi) == libcdoc::OK); |
| 380 | + BOOST_TEST(fi.name == files[cfile].name); |
| 381 | + BOOST_TEST(fi.size == files[cfile].size); |
| 382 | + for (size_t pos = 0; pos < files[cfile].size; pos += wrt.BUFSIZE) { |
| 383 | + // Fill buffer |
| 384 | + while((data.size() < 2 * wrt.BUFSIZE) && !wrt.isEof()) { |
| 385 | + BOOST_TEST(wrt.writeMore() == libcdoc::OK); |
| 386 | + } |
| 387 | + size_t toread = files[cfile].size - pos; |
| 388 | + if (toread > wrt.BUFSIZE) toread = wrt.BUFSIZE; |
| 389 | + uint8_t buf[wrt.BUFSIZE], cbuf[wrt.BUFSIZE]; |
| 390 | + BOOST_TEST(reader->readData(buf, toread) == toread); |
| 391 | + for (size_t i = 0; i < toread; i++) cbuf[i] = wrt.getChar(cfile, pos + i); |
| 392 | + BOOST_TEST(std::memcmp(buf, cbuf, toread) == 0); |
| 393 | + } |
| 394 | + } |
| 395 | + BOOST_TEST(reader->nextFile(fi) == libcdoc::END_OF_STREAM); |
| 396 | + BOOST_TEST(reader->finishDecryption() == libcdoc::OK); |
| 397 | +} |
| 398 | + |
| 399 | +BOOST_AUTO_TEST_SUITE_END() |
218 | 400 |
|
219 | 401 | BOOST_AUTO_TEST_SUITE(PasswordUsageWithLabel) |
220 | 402 |
|
|
0 commit comments