diff --git a/cdoc/CDoc2Writer.cpp b/cdoc/CDoc2Writer.cpp index e4363b6..6c81f69 100644 --- a/cdoc/CDoc2Writer.cpp +++ b/cdoc/CDoc2Writer.cpp @@ -537,18 +537,25 @@ CDoc2Writer::finishEncryption() libcdoc::result_t CDoc2Writer::encrypt(libcdoc::MultiDataSource& src, const std::vector& keys) { + for (auto rcpt : keys) { + if(auto rv = addRecipient(rcpt); rv != libcdoc::OK) + return rv; + } if(auto rv = beginEncryption(); rv < 0) return rv; - if(auto rv = writeHeader(keys); rv < 0) - return rv; std::string name; int64_t size; - while(src.next(name, size) == libcdoc::OK) { - if(tar->open(name, size) < 0 || tar->writeAll(src) < 0) - { - tar.reset(); - return libcdoc::IO_ERROR; - } + auto result = src.next(name, size); + while(result == libcdoc::OK) { + if (result = tar->open(name, size); result != libcdoc::OK) + break; + if (result = tar->writeAll(src); result != libcdoc::OK) + break; + result = src.next(name, size); + } + if (result != libcdoc::END_OF_STREAM) { + tar.reset(); + return result; } return finishEncryption(); } diff --git a/cdoc/Tar.cpp b/cdoc/Tar.cpp index 34331d1..572bf09 100644 --- a/cdoc/Tar.cpp +++ b/cdoc/Tar.cpp @@ -316,14 +316,25 @@ libcdoc::TarSource::next(std::string& name, int64_t& size) std::stringstream ss(paxData); for(const std::string &data: split(paxData, '\n')) { if(data.empty()) break; - const auto &headerValue = split(data, '='); - const auto &lenKeyword = split(headerValue[0], ' '); - if(data.size() + 1 != stoi(lenKeyword[0])) { + size_t eq_pos = data.find_first_of('='); + if (eq_pos == std::string::npos) { _error = DATA_FORMAT_ERROR; return _error; } - if(lenKeyword[1] == "path") h_name = headerValue[1]; - if(lenKeyword[1] == "size") h_size = stoi(headerValue[1]); + std::string headerValue = data.substr(eq_pos + 1, data.size() - eq_pos - 1); + size_t sp_pos = data.find_first_of(' '); + if ((sp_pos == std::string::npos) || (sp_pos >= eq_pos)) { + _error = DATA_FORMAT_ERROR; + return _error; + } + std::string lenStr = data.substr(0, sp_pos); + std::string keyWord = data.substr(sp_pos + 1, eq_pos - sp_pos - 1); + if(data.size() + 1 != stoi(lenStr)) { + _error = DATA_FORMAT_ERROR; + return _error; + } + if(keyWord == "path") h_name = std::move(headerValue); + if(keyWord == "size") h_size = stoi(headerValue); } } if(h.typeflag == '0' || h.typeflag == 0) { diff --git a/cdoc/cdoc-tool.cpp b/cdoc/cdoc-tool.cpp index ab88712..a3043dd 100644 --- a/cdoc/cdoc-tool.cpp +++ b/cdoc/cdoc-tool.cpp @@ -502,6 +502,18 @@ static int ParseAndDecrypt(int argc, char *argv[]) conf.out = "."; } + // Ask secret if not provided + if (ldata.secret[0] == '?') { + ldata.secret.clear(); + cout << "Enter secret: "; + int ch = std::getchar(); + while (ch != '\n') { + ldata.secret.push_back((uint8_t) ch); + ch = std::getchar(); + } + cout << std::endl; + } + CDocCipher cipher; RcptInfo rcpt {RcptInfo::ANY, {}, ldata.secret, ldata.slot, ldata.key_id, ldata.key_label}; if (ldata.lock_idx != -1) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 51d7ca8..4b60ec6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,6 +1,15 @@ -add_executable(unittests libcdoc_boost.cpp ../cdoc/CDocCipher.cpp ../cdoc/Crypto.cpp) -target_compile_definitions(unittests PRIVATE DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data") -target_link_libraries(unittests OpenSSL::SSL cdoc Boost::unit_test_framework) +add_executable(unittests + libcdoc_boost.cpp + ../cdoc/CDocCipher.cpp + ../cdoc/Crypto.cpp) + +target_compile_definitions(unittests PRIVATE + DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data") + +target_link_libraries(unittests + OpenSSL::SSL + cdoc + Boost::unit_test_framework) add_test(NAME runtest COMMAND ${CMAKE_CURRENT_BINARY_DIR}/unittests --build_info=YES --logger=HRF,all,stdout diff --git a/test/libcdoc_boost.cpp b/test/libcdoc_boost.cpp index 485cfbb..d9ff9ea 100644 --- a/test/libcdoc_boost.cpp +++ b/test/libcdoc_boost.cpp @@ -28,7 +28,8 @@ #include #include #include -#include + +#include "pipe.h" #ifndef DATA_DIR #define DATA_DIR "." @@ -228,110 +229,57 @@ class DecryptFixture : public FixtureBase } }; -struct PipeSource : public libcdoc::DataSource { - PipeSource(std::vector& data, bool& eof) : _data(data), _eof(eof) {} - - libcdoc::result_t read(uint8_t *dst, size_t size) override { - size = std::min(size, _data.size()); - std::copy(_data.cbegin(), _data.cbegin() + size, dst); - if (_buf.size() < 1024) { - size_t newbufsize = _buf.size() + size; - if (newbufsize > 1024) newbufsize = 1024; - size_t tocopy = newbufsize - _buf.size(); - _buf.insert(_buf.end(), _data.begin(), _data.begin() + tocopy); - } - _data.erase(_data.cbegin(), _data.cbegin() + size); - return size; - } - - libcdoc::result_t seek(size_t pos) override { - if (pos <= _buf.size()) { - _data.insert(_data.begin(), _buf.begin() + pos, _buf.end()); - _buf.erase(_buf.begin() + pos, _buf.end()); - return libcdoc::OK; - } - return libcdoc::NOT_IMPLEMENTED; - } - bool isError() override { return false; } - bool isEof() override { return _eof; } -protected: - std::vector& _data; - bool& _eof; - std::vector _buf; -}; - -struct PipeConsumer : public libcdoc::DataConsumer { - PipeConsumer(std::vector& data, bool& eof) : _data(data), _eof(eof) { _eof = false; } - libcdoc::result_t write(const uint8_t *src, size_t size) override final { - _data.insert(_data.end(), src, src + size); - return size; +static int +unicode_to_utf8 (unsigned int uval, uint8_t *d, uint64_t size) +{ + if ((uval < 0x80) && (size >= 1)) { + d[0] = (uint8_t) uval; + return 1; + } else if ((uval < 0x800) && (size >= 2)) { + d[0] = 0xc0 | (uval >> 6); + d[1] = 0x80 | (uval & 0x3f); + return 2; + } else if ((uval < 0x10000) && (size >= 3)) { + d[0] = 0xe0 | (uval >> 12); + d[1] = 0x80 | ((uval >> 6) & 0x3f); + d[2] = 0x80 | (uval & 0x3f); + return 3; + } else if ((uval < 0x110000) && (size >= 4)) { + d[0] = 0xf0 | (uval >> 18); + d[1] = 0x80 | ((uval >> 12) & 0x3f); + d[2] = 0x80 | ((uval >> 6) & 0x3f); + d[3] = 0x80 | (uval & 0x3f); + return 4; } - libcdoc::result_t close() override final { _eof = true; return libcdoc::OK; } - virtual bool isError() override final { return false; } -protected: - std::vector& _data; - bool& _eof; -}; - -struct PipeCrypto : public libcdoc::CryptoBackend { - PipeCrypto(std::string pwd) : _secret(pwd.cbegin(), pwd.cend()) {} - - libcdoc::result_t getSecret(std::vector& dst, unsigned int idx) { - dst = _secret; - return libcdoc::OK; - }; - - std::vector _secret; -}; - -struct PipeWriter { - static constexpr size_t BUFSIZE = 1024 * 1024; - - PipeWriter(libcdoc::CDocWriter *writer, const std::vector& files) : _writer(writer), _files(files), current(-1), cpos(0) {} - - uint8_t getChar(int filenum, size_t pos) { - uint64_t x = pos + ((uint64_t) filenum << 40); - x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL; - x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL; - x = x ^ (x >> 31); - return (uint8_t) (x & 0xff); - } - - libcdoc::result_t writeMore() { - if (current >= (int) _files.size()) return libcdoc::WORKFLOW_ERROR; - - if ((current < 0) || (cpos >= _files[current].size)) { - // Start new file - current += 1; - cpos = 0; - if (current >= (int) _files.size()) { - return _writer->finishEncryption(); - } - return _writer->addFile(_files[current].name, _files[current].size); - } - size_t towrite = _files[current].size - cpos; - if (towrite > BUFSIZE) towrite = BUFSIZE; - uint8_t buf[BUFSIZE]; - for (int i = 0; i < towrite; i++) buf[i] = getChar(current, cpos + i); - cpos += towrite; - return _writer->writeData(buf, towrite); - } + return 0; +} - bool isEof() { - return current >= (int) _files.size(); +static std::string +utf16_to_utf8(const std::u16string& utf16) +{ + std::string utf8; + for (char16_t c16 : utf16) { + char c[4]; + utf8.append(c, unicode_to_utf8(c16, (uint8_t *) c, 4)); } + return utf8; +} - int current = 0; - size_t cpos = 0; - - libcdoc::CDocWriter *_writer; - const std::vector& _files; -}; +static std::string +gen_random_filename() +{ + size_t len = std::rand() % 1000 + 1; + std::u16string u16(len, ' '); + for (int i = 0; i < len; i++) u16[i] = std::rand() % 10000 + 32; + return utf16_to_utf8(u16); +} BOOST_AUTO_TEST_SUITE(LargeFiles) BOOST_FIXTURE_TEST_CASE_WITH_DECOR(EncryptWithPasswordAndLabel, FixtureBase, * utf::description("Testing weird and large files")) { + std::srand(1); + std::vector data; bool eof = false; PipeConsumer pipec(data, eof); @@ -345,16 +293,14 @@ BOOST_FIXTURE_TEST_CASE_WITH_DECOR(EncryptWithPasswordAndLabel, FixtureBase, * u BOOST_TEST(writer->addRecipient(rcpt) == libcdoc::OK); BOOST_TEST(writer->beginEncryption() == libcdoc::OK); - std::srand(1); + // List of files: 0, 0, max_size...0 std::vector files; - for (size_t i = max_filesize; i != 0; i = i / 1000) { - size_t len = std::rand() % 1000; - std::u16string u16(len, ' '); - for (int i = 0; i < len; i++) u16[i] = std::rand() % 10000 + 32; - std::string u8 = std::wstring_convert, char16_t>{}.to_bytes(u16); - files.emplace_back(u8, i); - files.emplace_back(u8, 0); + files.emplace_back(gen_random_filename(), 0); + files.emplace_back(gen_random_filename(), 0); + for (size_t size = max_filesize; size != 0; size = size / 100) { + files.emplace_back(gen_random_filename(), size); } + files.emplace_back(gen_random_filename(), 0); PipeWriter wrt(writer, files); diff --git a/test/pipe.h b/test/pipe.h new file mode 100644 index 0000000..ed124a7 --- /dev/null +++ b/test/pipe.h @@ -0,0 +1,121 @@ +#pragma once + +/* + * libcdoc + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + #include + + struct PipeSource : public libcdoc::DataSource { + PipeSource(std::vector& data, bool& eof) : _data(data), _eof(eof) {} + + libcdoc::result_t read(uint8_t *dst, size_t size) override { + size = std::min(size, _data.size()); + std::copy(_data.cbegin(), _data.cbegin() + size, dst); + if (_buf.size() < 1024) { + size_t newbufsize = _buf.size() + size; + if (newbufsize > 1024) newbufsize = 1024; + size_t tocopy = newbufsize - _buf.size(); + _buf.insert(_buf.end(), _data.begin(), _data.begin() + tocopy); + } + _data.erase(_data.cbegin(), _data.cbegin() + size); + return size; + } + + libcdoc::result_t seek(size_t pos) override { + if (pos <= _buf.size()) { + _data.insert(_data.begin(), _buf.begin() + pos, _buf.end()); + _buf.erase(_buf.begin() + pos, _buf.end()); + return libcdoc::OK; + } + return libcdoc::NOT_IMPLEMENTED; + } + bool isError() override { return false; } + bool isEof() override { return _eof; } +protected: + std::vector& _data; + bool& _eof; + std::vector _buf; +}; + +struct PipeConsumer : public libcdoc::DataConsumer { + PipeConsumer(std::vector& data, bool& eof) : _data(data), _eof(eof) { _eof = false; } + libcdoc::result_t write(const uint8_t *src, size_t size) override final { + _data.insert(_data.end(), src, src + size); + return size; + } + libcdoc::result_t close() override final { _eof = true; return libcdoc::OK; } + virtual bool isError() override final { return false; } +protected: + std::vector& _data; + bool& _eof; +}; + +struct PipeCrypto : public libcdoc::CryptoBackend { + PipeCrypto(std::string pwd) : _secret(pwd.cbegin(), pwd.cend()) {} + + libcdoc::result_t getSecret(std::vector& dst, unsigned int idx) { + dst = _secret; + return libcdoc::OK; + }; + + std::vector _secret; +}; + +struct PipeWriter { + static constexpr size_t BUFSIZE = 1024 * 1024; + + PipeWriter(libcdoc::CDocWriter *writer, const std::vector& files) : _writer(writer), _files(files), current(-1), cpos(0) {} + + uint8_t getChar(int filenum, size_t pos) { + uint64_t x = pos + ((uint64_t) filenum << 40); + x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL; + x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL; + x = x ^ (x >> 31); + return (uint8_t) (x & 0xff); + } + + libcdoc::result_t writeMore() { + if (current >= (int) _files.size()) return libcdoc::WORKFLOW_ERROR; + + if ((current < 0) || (cpos >= _files[current].size)) { + // Start new file + current += 1; + cpos = 0; + if (current >= (int) _files.size()) { + return _writer->finishEncryption(); + } + return _writer->addFile(_files[current].name, _files[current].size); + } + size_t towrite = _files[current].size - cpos; + if (towrite > BUFSIZE) towrite = BUFSIZE; + uint8_t buf[BUFSIZE]; + for (int i = 0; i < towrite; i++) buf[i] = getChar(current, cpos + i); + cpos += towrite; + return _writer->writeData(buf, towrite); + } + + bool isEof() { + return current >= (int) _files.size(); + } + + int current = 0; + size_t cpos = 0; + + libcdoc::CDocWriter *_writer; + const std::vector& _files; +};