From 25ab00796435f45c492d7883f0ccf56f2fea7879 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 23 Jan 2026 16:42:05 +0200 Subject: [PATCH 01/16] Tar and workflow fixes and tests Signed-off-by: Lauris Kaplinski --- cdoc/CDoc.h | 9 ++ cdoc/CDoc2Reader.cpp | 80 ++++++++---- cdoc/CDoc2Writer.cpp | 42 +++++- cdoc/CDoc2Writer.h | 1 + cdoc/CryptoBackend.cpp | 4 +- cdoc/Io.h | 2 +- cdoc/Tar.cpp | 40 ++++-- test/libcdoc_boost.cpp | 289 ++++++++++++++++++++++++++++++++--------- 8 files changed, 367 insertions(+), 100 deletions(-) diff --git a/cdoc/CDoc.h b/cdoc/CDoc.h index 3ebe4e2f..7bad17c7 100644 --- a/cdoc/CDoc.h +++ b/cdoc/CDoc.h @@ -57,10 +57,16 @@ enum { NOT_SUPPORTED = -101, /** * @brief Conflicting or invalid arguments for a method + * + * This does not set CDocReader/CDocWriter into error state - so invoking subsequent methods + * with correct arguments will succeed */ WRONG_ARGUMENTS = -102, /** * @brief Components of multi-method workflow are called in wrong order + * + * This does not set CDocReader/CDocWriter into error state - so invoking subsequent methods + * in correct order will succeed */ WORKFLOW_ERROR = -103, /** @@ -85,6 +91,9 @@ enum { INPUT_STREAM_ERROR = -108, /** * @brief The supplied decryption key is wrong + * + * This does not set CDocReader/CDocWriter into error state - so invoking subsequent methods + * with correct key will succeed */ WRONG_KEY = -109, /** diff --git a/cdoc/CDoc2Reader.cpp b/cdoc/CDoc2Reader.cpp index 0f301ce6..1389c749 100644 --- a/cdoc/CDoc2Reader.cpp +++ b/cdoc/CDoc2Reader.cpp @@ -87,6 +87,20 @@ struct CDoc2Reader::Private { std::unique_ptr dec; std::unique_ptr zsrc; std::unique_ptr tar; + + result_t decryptAllAndClose() { + std::array buf; + result_t rv = dec->read(buf.data(), buf.size()); + while (rv == buf.size()) { + rv = dec->read(buf.data(), buf.size()); + } + if (rv < 0) return rv; + zsrc.reset(); + tar.reset(); + rv = dec->close(); + dec.reset(); + return rv; + } }; CDoc2Reader::~CDoc2Reader() @@ -118,35 +132,44 @@ CDoc2Reader::getLockForCert(const std::vector& cert){ libcdoc::result_t CDoc2Reader::getFMK(std::vector& fmk, unsigned int lock_idx) { + if (lock_idx >= priv->locks.size()) { + setLastError(t_("Invalid lock index")); + LOG_ERROR("{}", last_error); + return libcdoc::WRONG_ARGUMENTS; + } LOG_DBG("CDoc2Reader::getFMK: {}", lock_idx); LOG_DBG("CDoc2Reader::num locks: {}", priv->locks.size()); const Lock& lock = priv->locks.at(lock_idx); + LOG_DBG("Label: {}", lock.label); std::vector kek; if (lock.type == Lock::Type::PASSWORD) { // Password LOG_DBG("password"); std::string info_str = libcdoc::CDoc2::getSaltForExpand(lock.label); + LOG_DBG("info: {}", toHex(info_str)); std::vector kek_pm; - crypto->extractHKDF(kek_pm, lock.getBytes(Lock::SALT), lock.getBytes(Lock::PW_SALT), lock.getInt(Lock::KDF_ITER), lock_idx); - LOG_DBG("password2"); + if (auto rv = crypto->extractHKDF(kek_pm, lock.getBytes(Lock::SALT), lock.getBytes(Lock::PW_SALT), lock.getInt(Lock::KDF_ITER), lock_idx); rv != libcdoc::OK) { + setLastError(crypto->getLastErrorStr(rv)); + LOG_ERROR("{}", last_error); + return rv; + } + LOG_TRACE_KEY("salt: {}", lock.getBytes(Lock::SALT)); + LOG_TRACE_KEY("kek_pm: {}", kek_pm); kek = libcdoc::Crypto::expand(kek_pm, info_str, 32); - if (kek.empty()) return libcdoc::CRYPTO_ERROR; - LOG_DBG("password3"); } else if (lock.type == Lock::Type::SYMMETRIC_KEY) { // Symmetric key LOG_DBG("symmetric"); std::string info_str = libcdoc::CDoc2::getSaltForExpand(lock.label); - std::vector kek_pm; - crypto->extractHKDF(kek_pm, lock.getBytes(Lock::SALT), {}, 0, lock_idx); - kek = libcdoc::Crypto::expand(kek_pm, info_str, 32); - - LOG_DBG("Label: {}", lock.label); LOG_DBG("info: {}", toHex(info_str)); + std::vector kek_pm; + if (auto rv = crypto->extractHKDF(kek_pm, lock.getBytes(Lock::SALT), {}, 0, lock_idx); rv != libcdoc::OK) { + setLastError(crypto->getLastErrorStr(rv)); + LOG_ERROR("{}", last_error); + return rv; + } LOG_TRACE_KEY("salt: {}", lock.getBytes(Lock::SALT)); LOG_TRACE_KEY("kek_pm: {}", kek_pm); - LOG_TRACE_KEY("kek: {}", kek); - - if (kek.empty()) return libcdoc::CRYPTO_ERROR; + kek = libcdoc::Crypto::expand(kek_pm, info_str, 32); } else if ((lock.type == Lock::Type::PUBLIC_KEY) || (lock.type == Lock::Type::SERVER)) { // Public/private key std::vector key_material; @@ -196,13 +219,9 @@ CDoc2Reader::getFMK(std::vector& fmk, unsigned int lock_idx) LOG_ERROR("{}", last_error); return result; } - LOG_TRACE_KEY("Key kekPm: {}", kek_pm); - std::string info_str = libcdoc::CDoc2::getSaltForExpand(key_material, lock.getBytes(Lock::Params::RCPT_KEY)); - LOG_DBG("info: {}", toHex(info_str)); - kek = libcdoc::Crypto::expand(kek_pm, info_str, libcdoc::CDoc2::KEY_LEN); } } else if (lock.type == Lock::Type::SHARE_SERVER) { @@ -312,7 +331,6 @@ CDoc2Reader::getFMK(std::vector& fmk, unsigned int lock_idx) LOG_TRACE_KEY("KEK: {}", kek); - if(kek.empty()) { setLastError(t_("Failed to derive KEK")); LOG_ERROR("{}", last_error); @@ -394,10 +412,10 @@ CDoc2Reader::beginDecryption(const std::vector& fmk) std::vector aad(libcdoc::CDoc2::PAYLOAD.cbegin(), libcdoc::CDoc2::PAYLOAD.cend()); aad.insert(aad.end(), priv->header_data.cbegin(), priv->header_data.cend()); aad.insert(aad.end(), priv->headerHMAC.cbegin(), priv->headerHMAC.cend()); - if(priv->dec->updateAAD(aad) != OK) { - setLastError("Wrong decryption key (FMK)"); + if(auto rv = priv->dec->updateAAD(aad); rv != OK) { + setLastError(priv->dec->getLastErrorStr(rv)); LOG_ERROR("{}", last_error); - return libcdoc::WRONG_KEY; + return rv; } priv->zsrc = std::make_unique(priv->dec.get(), false); @@ -414,8 +432,13 @@ CDoc2Reader::nextFile(std::string& name, int64_t& size) LOG_ERROR("{}", last_error); return libcdoc::WORKFLOW_ERROR; } - result_t result = priv->tar->next(name, size); - if (result != OK) { + result_t result = priv->tar->next(name, size); + if (result < 0) { + result_t sr = priv->decryptAllAndClose(); + if (sr != OK) { + setLastError("Crypto payload integrity check failed"); + return sr; + } setLastError(priv->tar->getLastErrorStr(result)); } return result; @@ -430,7 +453,12 @@ CDoc2Reader::readData(uint8_t *dst, size_t size) return libcdoc::WORKFLOW_ERROR; } result_t result = priv->tar->read(dst, size); - if (result != OK) { + if (result < 0) { + result_t sr = priv->decryptAllAndClose(); + if (sr != OK) { + setLastError("Crypto payload integrity check failed"); + return sr; + } setLastError(priv->tar->getLastErrorStr(result)); } return result; @@ -439,11 +467,15 @@ CDoc2Reader::readData(uint8_t *dst, size_t size) libcdoc::result_t CDoc2Reader::finishDecryption() { + if (!priv->tar) { + setLastError("finishDecryption() called before beginDecryption()"); + LOG_ERROR("{}", last_error); + return libcdoc::WORKFLOW_ERROR; + } if (!priv->zsrc->isEof()) { setLastError(t_("CDoc contains additional payload data that is not part of content")); LOG_WARN("{}", last_error); } - setLastError({}); priv->zsrc.reset(); priv->tar.reset(); diff --git a/cdoc/CDoc2Writer.cpp b/cdoc/CDoc2Writer.cpp index 66942284..ba7f886e 100644 --- a/cdoc/CDoc2Writer.cpp +++ b/cdoc/CDoc2Writer.cpp @@ -457,6 +457,11 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vector 8ULL * 1024 * 1024 * 1024) { + setLastError("Invalid file size"); + LOG_ERROR("{}", last_error); + return libcdoc::WRONG_ARGUMENTS; + } if(auto rv = tar->open(name, size); rv < 0) { setLastError(tar->getLastErrorStr(rv)); LOG_ERROR("{}", last_error); @@ -505,6 +529,11 @@ CDoc2Writer::addFile(const std::string& name, size_t size) libcdoc::result_t CDoc2Writer::writeData(const uint8_t *src, size_t size) { + if (finished) { + setLastError("Encryption finished"); + LOG_ERROR("{}", last_error); + return libcdoc::WORKFLOW_ERROR; + } if(!tar) { setLastError("No file added"); LOG_ERROR("{}", last_error); @@ -520,6 +549,11 @@ CDoc2Writer::writeData(const uint8_t *src, size_t size) libcdoc::result_t CDoc2Writer::finishEncryption() { + if (finished) { + setLastError("Encryption finished"); + LOG_ERROR("{}", last_error); + return libcdoc::WORKFLOW_ERROR; + } if(!tar) { setLastError("No file added"); LOG_ERROR("{}", last_error); @@ -531,12 +565,18 @@ CDoc2Writer::finishEncryption() tar.reset(); recipients.clear(); if (owned) dst->close(); + finished = true; return rv; } libcdoc::result_t CDoc2Writer::encrypt(libcdoc::MultiDataSource& src, const std::vector& keys) { + if (finished) { + setLastError("Encryption finished"); + LOG_ERROR("{}", last_error); + return libcdoc::WORKFLOW_ERROR; + } for (auto rcpt : keys) { if(auto rv = addRecipient(rcpt); rv != libcdoc::OK) return rv; diff --git a/cdoc/CDoc2Writer.h b/cdoc/CDoc2Writer.h index f68acea2..3bb5800a 100644 --- a/cdoc/CDoc2Writer.h +++ b/cdoc/CDoc2Writer.h @@ -46,6 +46,7 @@ class CDoc2Writer final: public libcdoc::CDocWriter { std::unique_ptr tar; std::vector recipients; + bool finished = false; }; } diff --git a/cdoc/CryptoBackend.cpp b/cdoc/CryptoBackend.cpp index b0c10459..8c0f1652 100644 --- a/cdoc/CryptoBackend.cpp +++ b/cdoc/CryptoBackend.cpp @@ -82,7 +82,7 @@ CryptoBackend::getKeyMaterial(std::vector& key_material, const std::vec if (pw_salt.empty()) return INVALID_PARAMS; std::vector secret; int result = getSecret(secret, idx); - if (result < 0) return result; + if (result) return result; LOG_DBG("Secret: {}", toHex(secret)); @@ -91,7 +91,7 @@ CryptoBackend::getKeyMaterial(std::vector& key_material, const std::vec if (key_material.empty()) return OPENSSL_ERROR; } else { int result = getSecret(key_material, idx); - if (result < 0) return result; + if (result) return result; LOG_DBG("Secret: {}", toHex(key_material)); if (key_material.size() != 32) { return INVALID_PARAMS; diff --git a/cdoc/Io.h b/cdoc/Io.h index cdf9fd5c..45902101 100644 --- a/cdoc/Io.h +++ b/cdoc/Io.h @@ -255,7 +255,7 @@ struct CDOC_EXPORT IStreamSource : public DataSource { if (_owned) delete _ifs; } - result_t seek(size_t pos) { + result_t seek(size_t pos) override { if(_ifs->bad()) return INPUT_STREAM_ERROR; _ifs->clear(); _ifs->seekg(pos); diff --git a/cdoc/Tar.cpp b/cdoc/Tar.cpp index e1f79417..c4775fdb 100644 --- a/cdoc/Tar.cpp +++ b/cdoc/Tar.cpp @@ -130,6 +130,10 @@ libcdoc::TarConsumer::~TarConsumer() libcdoc::result_t libcdoc::TarConsumer::write(const uint8_t *src, size_t size) noexcept { + if ((_current_size >= 0) && ((_current_written + size) > _current_size)) { + return WORKFLOW_ERROR; + } + _current_written += size; return _dst->write(src, size); } @@ -160,19 +164,25 @@ libcdoc::TarConsumer::writePadding(int64_t size) noexcept { libcdoc::result_t libcdoc::TarConsumer::close() noexcept { - if (_current_size > 0) { - if(auto rv = writePadding(_current_size); rv != OK) - return rv; - } - Header empty = {}; - if(auto rv = writeHeader(empty); rv != OK) - return rv; - if(auto rv = writeHeader(empty); rv != OK) - return rv; + result_t result = OK; + if ((_current_size >= 0) && (_current_written < _current_size)) { + result = DATA_FORMAT_ERROR; + } else { + if (_current_written > 0) { + if(auto rv = writePadding(_current_written); rv != OK) + return rv; + } + Header empty = {}; + if(auto rv = writeHeader(empty); rv != OK) + return rv; + if(auto rv = writeHeader(empty); rv != OK) + return rv; + } if (_owned) { - return _dst->close(); + if (auto rv = _dst->close(); rv != OK) + return rv; } - return OK; + return result; } bool @@ -184,12 +194,16 @@ libcdoc::TarConsumer::isError() noexcept libcdoc::result_t libcdoc::TarConsumer::open(const std::string& name, int64_t size) { - if (_current_size > 0) { - if(auto rv = writePadding(_current_size); rv != OK) + if ((_current_size >= 0) && (_current_written < _current_size)) { + return WORKFLOW_ERROR; + } + if (_current_written > 0) { + if(auto rv = writePadding(_current_written); rv != OK) return rv; } _current_size = size; + _current_written = 0; Header h {}; size_t len = std::min(name.size(), h.name.size()); std::copy_n(name.cbegin(), len, h.name.begin()); diff --git a/test/libcdoc_boost.cpp b/test/libcdoc_boost.cpp index 39035652..3b48536e 100644 --- a/test/libcdoc_boost.cpp +++ b/test/libcdoc_boost.cpp @@ -344,74 +344,173 @@ gen_random_filename() 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); +// CDoc2 password and label - std::vector data; - bool eof = false; - PipeConsumer pipec(data, eof); - PipeSource pipes(data, eof); - PipeCrypto pcrypto("password"); +struct TestCrypto : public libcdoc::CryptoBackend { + std::string_view password; - // Create writer - libcdoc::CDocWriter *writer = libcdoc::CDocWriter::createWriter(2, &pipec, false, nullptr, &pcrypto, nullptr); - BOOST_TEST(writer != nullptr); - libcdoc::Recipient rcpt = libcdoc::Recipient::makeSymmetric("test", 65536); - BOOST_TEST(writer->addRecipient(rcpt) == libcdoc::OK); - BOOST_TEST(writer->beginEncryption() == libcdoc::OK); + libcdoc::result_t getSecret(std::vector& dst, unsigned int idx) override final { + // Mark empty password with bogus error to detect it + if(password.empty()) return libcdoc::WRONG_ARGUMENTS; + dst.assign(password.cbegin(), password.cend()); + return libcdoc::OK; + }; +}; - // List of files: 0, 0, max_size...0 - std::vector files; - 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); +BOOST_AUTO_TEST_SUITE(CDoc2Errors) +BOOST_FIXTURE_TEST_CASE_WITH_DECOR(CDoc2EncryptErrors, EncryptFixture, + * utf::description("Cause various encryption errors")) +{ + std::string container = formTargetFile("CDoc2Errors.cdoc"); + uint8_t test_data[256]; - PipeWriter wrt(writer, files); + libcdoc::ToolConf conf; + TestCrypto crypto; - // Create reader - libcdoc::CDocReader *reader = libcdoc::CDocReader::createReader(&pipes, false, nullptr, &pcrypto, nullptr); - BOOST_TEST(reader != nullptr); + srand(0); + // Create writer + libcdoc::CDocWriter *wrt = libcdoc::CDocWriter::createWriter(2, container, &conf, &crypto, nullptr); + BOOST_TEST(wrt != nullptr, "Cannot create writer"); + // Nothing can be done until at least one recipient is added + BOOST_TEST(wrt->beginEncryption() == libcdoc::WORKFLOW_ERROR); + BOOST_TEST(wrt->addFile("testfile", 1024) == libcdoc::WORKFLOW_ERROR); + BOOST_TEST(wrt->writeData(test_data, 256) == libcdoc::WORKFLOW_ERROR); + BOOST_TEST(wrt->finishEncryption() == libcdoc::WORKFLOW_ERROR); + + // Add recipient + libcdoc::Recipient rcpt = libcdoc::Recipient::makeSymmetric("test-recipient", 65536); + BOOST_TEST(wrt->addRecipient(rcpt) == libcdoc::OK); + // Encryption cannot proceed before beginEncryption is called + BOOST_TEST(wrt->addFile("testfile", 1024) == libcdoc::WORKFLOW_ERROR); + BOOST_TEST(wrt->writeData(test_data, 256) == libcdoc::WORKFLOW_ERROR); + BOOST_TEST(wrt->finishEncryption() == libcdoc::WORKFLOW_ERROR); + + // Begin encryption + BOOST_TEST(wrt->beginEncryption() == libcdoc::WRONG_ARGUMENTS); + crypto.password = "test-password"; + BOOST_TEST(wrt->beginEncryption() == libcdoc::OK); + // Cannot do anything else than add files + BOOST_TEST(wrt->addRecipient(rcpt) == libcdoc::WORKFLOW_ERROR); + BOOST_TEST(wrt->beginEncryption() == libcdoc::WORKFLOW_ERROR); + BOOST_TEST(wrt->writeData(test_data, 256) == libcdoc::WORKFLOW_ERROR); + // Finish encryption will succeed with empty tar + + // Add file + BOOST_TEST(wrt->addFile("testfile", 1024) == libcdoc::OK); + // Errors + BOOST_TEST(wrt->addRecipient(rcpt) == libcdoc::WORKFLOW_ERROR); + BOOST_TEST(wrt->beginEncryption() == libcdoc::WORKFLOW_ERROR); + BOOST_TEST(wrt->addFile("testfile", 1024) == libcdoc::WORKFLOW_ERROR); + + // Write data + for (int i = 0; i < 256; i++) test_data[i] = uint8_t(rand() & 0xff); + BOOST_TEST(wrt->writeData(test_data, 256) == libcdoc::OK); + BOOST_TEST(wrt->addRecipient(rcpt) == libcdoc::WORKFLOW_ERROR); + BOOST_TEST(wrt->beginEncryption() == libcdoc::WORKFLOW_ERROR); + BOOST_TEST(wrt->addFile("testfile", 1024) == libcdoc::WORKFLOW_ERROR); + for (int i = 0; i < 256; i++) test_data[i] = uint8_t(rand() & 0xff); + BOOST_TEST(wrt->writeData(test_data, 256) == libcdoc::OK); + for (int i = 0; i < 256; i++) test_data[i] = uint8_t(rand() & 0xff); + BOOST_TEST(wrt->writeData(test_data, 256) == libcdoc::OK); + for (int i = 0; i < 256; i++) test_data[i] = uint8_t(rand() & 0xff); + BOOST_TEST(wrt->writeData(test_data, 256) == libcdoc::OK); + BOOST_TEST(wrt->writeData(test_data, 256) == libcdoc::WORKFLOW_ERROR); + BOOST_TEST(wrt->addRecipient(rcpt) == libcdoc::WORKFLOW_ERROR); + BOOST_TEST(wrt->beginEncryption() == libcdoc::WORKFLOW_ERROR); + // Add file with unknown size + BOOST_TEST(wrt->addFile("testfile2", 10000000000ULL) == libcdoc::WRONG_ARGUMENTS); + BOOST_TEST(wrt->addFile("testfile2", 255) == libcdoc::OK); + for (int i = 0; i < 256; i++) test_data[i] = uint8_t(rand() & 0xff); + BOOST_TEST(wrt->writeData(test_data, 255) == libcdoc::OK); + BOOST_TEST(wrt->addRecipient(rcpt) == libcdoc::WORKFLOW_ERROR); + BOOST_TEST(wrt->beginEncryption() == libcdoc::WORKFLOW_ERROR); + BOOST_TEST(wrt->finishEncryption() == libcdoc::OK); + + BOOST_TEST(wrt->addRecipient(rcpt) == libcdoc::WORKFLOW_ERROR); + BOOST_TEST(wrt->beginEncryption() == libcdoc::WORKFLOW_ERROR); + BOOST_TEST(wrt->addFile("testfile", 1024) == libcdoc::WORKFLOW_ERROR); + BOOST_TEST(wrt->writeData(test_data, 256) == libcdoc::WORKFLOW_ERROR); + BOOST_TEST(wrt->finishEncryption() == libcdoc::WORKFLOW_ERROR); + + delete wrt; +} - // Fill buffer - while((data.size() < 2 * wrt.BUFSIZE) && !wrt.isEof()) { - BOOST_TEST(wrt.writeMore() == libcdoc::OK); - } - std::vector fmk; - BOOST_TEST(reader->getFMK(fmk, 0) == libcdoc::OK); - BOOST_TEST(reader->beginDecryption(fmk) == libcdoc::OK); +BOOST_FIXTURE_TEST_CASE_WITH_DECOR(CDoc2DecryptErrors, DecryptFixture, + * utf::depends_on("CDoc2Errors/CDoc2EncryptErrors") + * utf::description("Cause various decryption errors")) +{ + std::string container = checkTargetFile("CDoc2Errors.cdoc"); + libcdoc::ToolConf conf; + TestCrypto crypto; + uint8_t buf[1024]; + + libcdoc::CDocReader *rdr = libcdoc::CDocReader::createReader(container, &conf, &crypto, nullptr); + BOOST_TEST(rdr != nullptr, "Cannot create reader"); + std::vector fmk(32); + BOOST_TEST(rdr->getFMK(fmk, 10) == libcdoc::WRONG_ARGUMENTS); + // Decryption should start with random key + BOOST_TEST(rdr->beginDecryption(fmk) == libcdoc::OK); libcdoc::FileInfo fi; - for (int cfile = 0; cfile < files.size(); cfile++) { - // Fill buffer - while((data.size() < 2 * wrt.BUFSIZE) && !wrt.isEof()) { - BOOST_TEST(wrt.writeMore() == libcdoc::OK); - } - // Get file - BOOST_TEST(reader->nextFile(fi) == libcdoc::OK); - BOOST_TEST(fi.name == files[cfile].name); - BOOST_TEST(fi.size == files[cfile].size); - for (size_t pos = 0; pos < files[cfile].size; pos += wrt.BUFSIZE) { - // Fill buffer - while((data.size() < 2 * wrt.BUFSIZE) && !wrt.isEof()) { - BOOST_TEST(wrt.writeMore() == libcdoc::OK); - } - size_t toread = files[cfile].size - pos; - if (toread > wrt.BUFSIZE) toread = wrt.BUFSIZE; - uint8_t buf[wrt.BUFSIZE], cbuf[wrt.BUFSIZE]; - BOOST_TEST(reader->readData(buf, toread) == toread); - for (size_t i = 0; i < toread; i++) cbuf[i] = wrt.getChar(cfile, pos + i); - BOOST_TEST(std::memcmp(buf, cbuf, toread) == 0); - } + // But the first file should file + BOOST_TEST(rdr->nextFile(fi) != libcdoc::OK); + delete rdr; + + rdr = libcdoc::CDocReader::createReader(container, &conf, &crypto, nullptr); + BOOST_TEST(rdr != nullptr, "Cannot create reader"); + BOOST_TEST(rdr->getFMK(fmk, 0) == libcdoc::WRONG_ARGUMENTS); + crypto.password = "wrong-password"; + BOOST_TEST(rdr->getFMK(fmk, 0) == libcdoc::WRONG_KEY); + crypto.password = "test-password"; + BOOST_TEST(rdr->getFMK(fmk, 0) == libcdoc::OK); + BOOST_TEST(rdr->beginDecryption(fmk) == libcdoc::OK); + BOOST_TEST(rdr->nextFile(fi) == libcdoc::OK); + BOOST_TEST(fi.size == 1024); + BOOST_TEST(rdr->readData(buf, 256) == 256); + BOOST_TEST(rdr->readData(buf, 256) == 256); + BOOST_TEST(rdr->readData(buf, 256) == 256); + BOOST_TEST(rdr->readData(buf, 1024) == 256); + BOOST_TEST(rdr->nextFile(fi) == libcdoc::OK); + BOOST_TEST(fi.size == 255); + BOOST_TEST(rdr->readData(buf, 1024) == 255); + BOOST_TEST(rdr->finishDecryption() == libcdoc::OK); + delete rdr; + + // Write over the end of file + size_t fsize = std::filesystem::file_size(container); + std::fstream file(container, std::ios::out | std::ios::in); + BOOST_TEST(!file.bad()); + file.seekp(fsize - 16, std::ios::beg); + file.write((char *) buf, 16); + file.close(); + + rdr = libcdoc::CDocReader::createReader(container, &conf, &crypto, nullptr); + BOOST_TEST(rdr != nullptr, "Cannot create reader"); + BOOST_TEST(rdr->getFMK(fmk, 0) == libcdoc::OK); + BOOST_TEST(rdr->beginDecryption(fmk) == libcdoc::OK); + BOOST_TEST(rdr->nextFile(fi) == libcdoc::OK); + BOOST_TEST(rdr->nextFile(fi) == libcdoc::OK); + BOOST_TEST(rdr->finishDecryption() == libcdoc::CRYPTO_ERROR); + delete rdr; + + // Truncate file, should result zlib error + std::filesystem::resize_file(container, fsize - 32); + rdr = libcdoc::CDocReader::createReader(container, &conf, &crypto, nullptr); + BOOST_TEST(rdr != nullptr, "Cannot create reader"); + BOOST_TEST(rdr->getFMK(fmk, 0) == libcdoc::OK); + BOOST_TEST(rdr->beginDecryption(fmk) == libcdoc::OK); + libcdoc::result_t rv = rdr->nextFile(fi); + BOOST_TEST(((rv == libcdoc::OK) || (rv == libcdoc::CRYPTO_ERROR))); + for (int i = 0; i < 4; i++) { + rv = rdr->readData(buf, 256); + BOOST_TEST(((rv == 256) || (rv == libcdoc::CRYPTO_ERROR))); } - BOOST_TEST(reader->nextFile(fi) == libcdoc::END_OF_STREAM); - BOOST_TEST(reader->finishDecryption() == libcdoc::OK); + rv = rdr->nextFile(fi); + BOOST_TEST(((rv == libcdoc::OK) || (rv == libcdoc::CRYPTO_ERROR))); + rv = rdr->readData(buf, 256); + BOOST_TEST(((rv == 255) || (rv == libcdoc::CRYPTO_ERROR))); + BOOST_TEST(rdr->finishDecryption() == libcdoc::WORKFLOW_ERROR); + delete rdr; } - BOOST_AUTO_TEST_SUITE_END() // CDoc2 password and label @@ -553,6 +652,78 @@ BOOST_FIXTURE_TEST_CASE_WITH_DECOR(DecryptWithRSAKeyV1, DecryptFixture, } BOOST_AUTO_TEST_SUITE_END() +// Stream encryption/decryption of large files + +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); + PipeSource pipes(data, eof); + PipeCrypto pcrypto("password"); + + // Create writer + libcdoc::CDocWriter *writer = libcdoc::CDocWriter::createWriter(2, &pipec, false, nullptr, &pcrypto, nullptr); + BOOST_TEST(writer != nullptr); + libcdoc::Recipient rcpt = libcdoc::Recipient::makeSymmetric("test", 65536); + BOOST_TEST(writer->addRecipient(rcpt) == libcdoc::OK); + BOOST_TEST(writer->beginEncryption() == libcdoc::OK); + + // List of files: 0, 0, max_size...0 + std::vector files; + 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); + + // Create reader + libcdoc::CDocReader *reader = libcdoc::CDocReader::createReader(&pipes, false, nullptr, &pcrypto, nullptr); + BOOST_TEST(reader != nullptr); + + // Fill buffer + while((data.size() < 2 * wrt.BUFSIZE) && !wrt.isEof()) { + BOOST_TEST(wrt.writeMore() == libcdoc::OK); + } + std::vector fmk; + BOOST_TEST(reader->getFMK(fmk, 0) == libcdoc::OK); + BOOST_TEST(reader->beginDecryption(fmk) == libcdoc::OK); + libcdoc::FileInfo fi; + for (int cfile = 0; cfile < files.size(); cfile++) { + // Fill buffer + while((data.size() < 2 * wrt.BUFSIZE) && !wrt.isEof()) { + BOOST_TEST(wrt.writeMore() == libcdoc::OK); + } + // Get file + BOOST_TEST(reader->nextFile(fi) == libcdoc::OK); + BOOST_TEST(fi.name == files[cfile].name); + BOOST_TEST(fi.size == files[cfile].size); + for (size_t pos = 0; pos < files[cfile].size; pos += wrt.BUFSIZE) { + // Fill buffer + while((data.size() < 2 * wrt.BUFSIZE) && !wrt.isEof()) { + BOOST_TEST(wrt.writeMore() == libcdoc::OK); + } + size_t toread = files[cfile].size - pos; + if (toread > wrt.BUFSIZE) toread = wrt.BUFSIZE; + uint8_t buf[wrt.BUFSIZE], cbuf[wrt.BUFSIZE]; + BOOST_TEST(reader->readData(buf, toread) == toread); + for (size_t i = 0; i < toread; i++) cbuf[i] = wrt.getChar(cfile, pos + i); + BOOST_TEST(std::memcmp(buf, cbuf, toread) == 0); + } + } + BOOST_TEST(reader->nextFile(fi) == libcdoc::END_OF_STREAM); + BOOST_TEST(reader->finishDecryption() == libcdoc::OK); +} + +BOOST_AUTO_TEST_SUITE_END() + // Label parsing BOOST_AUTO_TEST_SUITE(MachineLabelParsing) From b4a173662af1bdc42060e61b66ef9f1dab004eb2 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 30 Jan 2026 14:57:46 +0200 Subject: [PATCH 02/16] Create locks even if capsule is not supported --- cdoc/CDoc2Reader.cpp | 272 ++++++++++++++++++++--------------------- cdoc/Crypto.cpp | 2 +- cdoc/Lock.h | 7 +- test/libcdoc_boost.cpp | 10 +- 4 files changed, 148 insertions(+), 143 deletions(-) diff --git a/cdoc/CDoc2Reader.cpp b/cdoc/CDoc2Reader.cpp index 1389c749..91de38e7 100644 --- a/cdoc/CDoc2Reader.cpp +++ b/cdoc/CDoc2Reader.cpp @@ -101,6 +101,8 @@ struct CDoc2Reader::Private { dec.reset(); return rv; } + + static void buildLock(Lock& lock, const cdoc20::header::RecipientRecord& recipient); }; CDoc2Reader::~CDoc2Reader() @@ -487,11 +489,140 @@ CDoc2Reader::finishDecryption() return rv; } +void +CDoc2Reader::Private::buildLock(Lock& lock, const cdoc20::header::RecipientRecord& recipient) +{ + using namespace cdoc20::recipients; + using namespace cdoc20::header; + + lock.label = recipient.key_label()->str(); + lock.encrypted_fmk.assign(recipient.encrypted_fmk()->cbegin(), recipient.encrypted_fmk()->cend()); + + if(recipient.fmk_encryption_method() != cdoc20::header::FMKEncryptionMethod::XOR) + { + LOG_WARN("Unsupported FMK encryption method"); + return; + } + switch(recipient.capsule_type()) + { + case Capsule::recipients_ECCPublicKeyCapsule: + if(const auto *key = recipient.capsule_as_recipients_ECCPublicKeyCapsule()) { + if(key->curve() == EllipticCurve::secp384r1) { + lock.type = Lock::Type::PUBLIC_KEY; + lock.pk_type = Lock::PKType::ECC; + lock.setBytes(Lock::Params::RCPT_KEY, std::vector(key->recipient_public_key()->cbegin(), key->recipient_public_key()->cend())); + lock.setBytes(Lock::Params::KEY_MATERIAL, std::vector(key->sender_public_key()->cbegin(), key->sender_public_key()->cend())); + LOG_DBG("Load PK: {}", toHex(lock.getBytes(Lock::Params::RCPT_KEY))); + } else { + LOG_ERROR("Unsupported ECC curve: skipping"); + } + } + return; + case Capsule::recipients_RSAPublicKeyCapsule: + if(const auto *key = recipient.capsule_as_recipients_RSAPublicKeyCapsule()) + { + lock.type = Lock::Type::PUBLIC_KEY; + lock.pk_type = Lock::PKType::RSA; + lock.setBytes(Lock::Params::RCPT_KEY, std::vector(key->recipient_public_key()->cbegin(), key->recipient_public_key()->cend())); + lock.setBytes(Lock::Params::KEY_MATERIAL, std::vector(key->encrypted_kek()->cbegin(), key->encrypted_kek()->cend())); + } + return; + case Capsule::recipients_KeyServerCapsule: + if (const KeyServerCapsule *server = recipient.capsule_as_recipients_KeyServerCapsule()) { + KeyDetailsUnion details = server->recipient_key_details_type(); + switch (details) { + case KeyDetailsUnion::EccKeyDetails: + if(const EccKeyDetails *eccDetails = server->recipient_key_details_as_EccKeyDetails()) { + if(eccDetails->curve() != EllipticCurve::secp384r1) { + LOG_ERROR("Unsupported elliptic curve key type"); + return; + } + lock.pk_type = Lock::PKType::ECC; + lock.setBytes(Lock::Params::RCPT_KEY, std::vector(eccDetails->recipient_public_key()->cbegin(), eccDetails->recipient_public_key()->cend())); + } else { + LOG_ERROR("Invalid file format"); + return; + } + break; + case KeyDetailsUnion::RsaKeyDetails: + if(const RsaKeyDetails *rsaDetails = server->recipient_key_details_as_RsaKeyDetails()) { + lock.pk_type = Lock::PKType::RSA; + lock.setBytes(Lock::Params::RCPT_KEY, std::vector(rsaDetails->recipient_public_key()->cbegin(), rsaDetails->recipient_public_key()->cend())); + } else { + LOG_ERROR("Invalid file format"); + return; + } + break; + default: + LOG_ERROR("Unsupported Key Server Details: skipping"); + return; + } + lock.type = Lock::Type::SERVER; + lock.setString(Lock::Params::KEYSERVER_ID, server->keyserver_id()->str()); + lock.setString(Lock::Params::TRANSACTION_ID, server->transaction_id()->str()); + } else { + LOG_ERROR("Invalid file format"); + } + return; + case Capsule::recipients_SymmetricKeyCapsule: + if(const auto *capsule = recipient.capsule_as_recipients_SymmetricKeyCapsule()) + { + lock.type = Lock::SYMMETRIC_KEY; + lock.setBytes(Lock::SALT, std::vector(capsule->salt()->cbegin(), capsule->salt()->cend())); + } + return; + case Capsule::recipients_PBKDF2Capsule: + if(const auto *capsule = recipient.capsule_as_recipients_PBKDF2Capsule()) { + KDFAlgorithmIdentifier kdf_id = capsule->kdf_algorithm_identifier(); + if (kdf_id != KDFAlgorithmIdentifier::PBKDF2WithHmacSHA256) { + LOG_ERROR("Unsupported KDF algorithm: skipping"); + return; + } + lock.type = Lock::PASSWORD; + lock.setBytes(Lock::SALT, std::vector(capsule->salt()->cbegin(), capsule->salt()->cend())); + lock.setBytes(Lock::PW_SALT, std::vector(capsule->password_salt()->cbegin(), capsule->password_salt()->cend())); + lock.setInt(Lock::KDF_ITER, capsule->kdf_iterations()); + } + return; + case Capsule::recipients_KeySharesCapsule: + if (const auto *capsule = recipient.capsule_as_recipients_KeySharesCapsule()) { + if (capsule->recipient_type() != cdoc20::recipients::KeyShareRecipientType::SID_MID) { + LOG_ERROR("Invalid keyshare recipient type: {}", (int) capsule->recipient_type()); + return; + } + if (capsule->shares_scheme() != cdoc20::recipients::SharesScheme::N_OF_N) { + LOG_ERROR("Invalid keyshare scheme type: {}", (int) capsule->shares_scheme()); + return; + } + /* url,share_id;url,share_id... */ + std::vector strs; + for (auto cshare = capsule->shares()->cbegin(); cshare != capsule->shares()->cend(); ++cshare) { + std::string id = cshare->share_id()->str(); + std::string url = cshare->server_base_url()->str(); + std::string str = url + "," + id; + LOG_DBG("Keyshare: {}", str); + strs.push_back(std::move(str)); + } + std::string urls = join(strs, ";"); + LOG_DBG("Keyshare urls: {}", urls); + std::vector salt(capsule->salt()->cbegin(), capsule->salt()->cend()); + LOG_DBG("Keyshare salt: {}", toHex(salt)); + std::string recipient_id = capsule->recipient_id()->str(); + LOG_DBG("Keyshare recipient id: {}", recipient_id); + lock.type = Lock::SHARE_SERVER; + lock.setString(Lock::SHARE_URLS, urls); + lock.setBytes(Lock::SALT, salt); + lock.setString(Lock::RECIPIENT_ID, recipient_id); + } + return; + default: + LOG_ERROR("Unsupported Key Details: skipping"); + } +} + CDoc2Reader::CDoc2Reader(libcdoc::DataSource *src, bool take_ownership) : CDocReader(2), priv(std::make_unique(src, take_ownership)) { - - using namespace cdoc20::recipients; using namespace cdoc20::header; setLastError(t_("Invalid CDoc 2.0 header")); @@ -555,140 +686,9 @@ CDoc2Reader::CDoc2Reader(libcdoc::DataSource *src, bool take_ownership) setLastError({}); for(const auto *recipient: *recipients){ - if(recipient->fmk_encryption_method() != FMKEncryptionMethod::XOR) - { - LOG_WARN("Unsupported FMK encryption method: skipping"); - continue; - } - auto fillRecipientPK = [&recipient,&locks = priv->locks] (Lock::PKType pk_type, auto key) -> Lock& { - Lock &k = locks.emplace_back(Lock::Type::PUBLIC_KEY); - k.pk_type = pk_type; - k.setBytes(Lock::Params::RCPT_KEY, std::vector(key->recipient_public_key()->cbegin(), key->recipient_public_key()->cend())); - k.label = recipient->key_label()->str(); - k.encrypted_fmk.assign(recipient->encrypted_fmk()->cbegin(), recipient->encrypted_fmk()->cend()); - return k; - }; - switch(recipient->capsule_type()) - { - case Capsule::recipients_ECCPublicKeyCapsule: - if(const auto *key = recipient->capsule_as_recipients_ECCPublicKeyCapsule()) { - if(key->curve() != EllipticCurve::secp384r1) { - LOG_ERROR("Unsupported ECC curve: skipping"); - continue; - } - Lock &k = fillRecipientPK(Lock::PKType::ECC, key); - k.setBytes(Lock::Params::KEY_MATERIAL, std::vector(key->sender_public_key()->cbegin(), key->sender_public_key()->cend())); - LOG_DBG("Load PK: {}", toHex(k.getBytes(Lock::Params::RCPT_KEY))); - } - break; - case Capsule::recipients_RSAPublicKeyCapsule: - if(const auto *key = recipient->capsule_as_recipients_RSAPublicKeyCapsule()) - { - Lock &k = fillRecipientPK(Lock::PKType::RSA, key); - k.setBytes(Lock::Params::KEY_MATERIAL, std::vector(key->encrypted_kek()->cbegin(), key->encrypted_kek()->cend())); - } - break; - case Capsule::recipients_KeyServerCapsule: - if (const KeyServerCapsule *server = recipient->capsule_as_recipients_KeyServerCapsule()) { - KeyDetailsUnion details = server->recipient_key_details_type(); - Lock ckey; - switch (details) { - case KeyDetailsUnion::EccKeyDetails: - if(const EccKeyDetails *eccDetails = server->recipient_key_details_as_EccKeyDetails()) { - if(eccDetails->curve() == EllipticCurve::secp384r1) { - ckey.type = Lock::Type::SERVER; - ckey.pk_type = Lock::PKType::ECC; - ckey.setBytes(Lock::Params::RCPT_KEY, std::vector(eccDetails->recipient_public_key()->cbegin(), eccDetails->recipient_public_key()->cend())); - } else { - LOG_ERROR("Unsupported elliptic curve key type"); - } - } else { - LOG_ERROR("Invalid file format"); - } - break; - case KeyDetailsUnion::RsaKeyDetails: - if(const RsaKeyDetails *rsaDetails = server->recipient_key_details_as_RsaKeyDetails()) { - ckey.type = Lock::Type::SERVER; - ckey.pk_type = Lock::PKType::RSA; - ckey.setBytes(Lock::Params::RCPT_KEY, std::vector(rsaDetails->recipient_public_key()->cbegin(), rsaDetails->recipient_public_key()->cend())); - } else { - LOG_ERROR("Invalid file format"); - } - break; - default: - LOG_ERROR("Unsupported Key Server Details: skipping"); - } - if (ckey.type != Lock::Type::INVALID) { - ckey.label = recipient->key_label()->c_str(); - ckey.encrypted_fmk.assign(recipient->encrypted_fmk()->cbegin(), recipient->encrypted_fmk()->cend()); - ckey.setString(Lock::Params::KEYSERVER_ID, server->keyserver_id()->str()); - ckey.setString(Lock::Params::TRANSACTION_ID, server->transaction_id()->str()); - priv->locks.push_back(std::move(ckey)); - } - } else { - LOG_ERROR("Invalid file format"); - } - break; - case Capsule::recipients_SymmetricKeyCapsule: - if(const auto *capsule = recipient->capsule_as_recipients_SymmetricKeyCapsule()) - { - Lock &key = priv->locks.emplace_back(Lock::SYMMETRIC_KEY); - key.label = recipient->key_label()->str(); - key.encrypted_fmk.assign(recipient->encrypted_fmk()->cbegin(), recipient->encrypted_fmk()->cend()); - key.setBytes(Lock::SALT, std::vector(capsule->salt()->cbegin(), capsule->salt()->cend())); - } - break; - case Capsule::recipients_PBKDF2Capsule: - if(const auto *capsule = recipient->capsule_as_recipients_PBKDF2Capsule()) { - KDFAlgorithmIdentifier kdf_id = capsule->kdf_algorithm_identifier(); - if (kdf_id != KDFAlgorithmIdentifier::PBKDF2WithHmacSHA256) { - LOG_ERROR("Unsupported KDF algorithm: skipping"); - continue; - } - Lock &key = priv->locks.emplace_back(Lock::PASSWORD); - key.label = recipient->key_label()->str(); - key.encrypted_fmk.assign(recipient->encrypted_fmk()->cbegin(), recipient->encrypted_fmk()->cend()); - key.setBytes(Lock::SALT, std::vector(capsule->salt()->cbegin(), capsule->salt()->cend())); - key.setBytes(Lock::PW_SALT, std::vector(capsule->password_salt()->cbegin(), capsule->password_salt()->cend())); - key.setInt(Lock::KDF_ITER, capsule->kdf_iterations()); - } - break; - case Capsule::recipients_KeySharesCapsule: - if (const auto *capsule = recipient->capsule_as_recipients_KeySharesCapsule()) { - if (capsule->recipient_type() != cdoc20::recipients::KeyShareRecipientType::SID_MID) { - LOG_ERROR("Invalid keyshare recipient type: {}", (int) capsule->recipient_type()); - continue; - } - if (capsule->shares_scheme() != cdoc20::recipients::SharesScheme::N_OF_N) { - LOG_ERROR("Invalid keyshare scheme type: {}", (int) capsule->shares_scheme()); - continue; - } - /* url,share_id;url,share_id... */ - std::vector strs; - for (auto cshare = capsule->shares()->cbegin(); cshare != capsule->shares()->cend(); ++cshare) { - std::string id = cshare->share_id()->str(); - std::string url = cshare->server_base_url()->str(); - std::string str = url + "," + id; - LOG_DBG("Keyshare: {}", str); - strs.push_back(std::move(str)); - } - std::string urls = join(strs, ";"); - LOG_DBG("Keyshare urls: {}", urls); - std::vector salt(capsule->salt()->cbegin(), capsule->salt()->cend()); - LOG_DBG("Keyshare salt: {}", toHex(salt)); - std::string recipient_id = capsule->recipient_id()->str(); - LOG_DBG("Keyshare recipient id: {}", recipient_id); - Lock &lock = priv->locks.emplace_back(Lock::SHARE_SERVER); - lock.label = recipient->key_label()->str(); - lock.encrypted_fmk.assign(recipient->encrypted_fmk()->cbegin(), recipient->encrypted_fmk()->cend()); - lock.setString(Lock::SHARE_URLS, urls); - lock.setBytes(Lock::SALT, salt); - lock.setString(Lock::RECIPIENT_ID, recipient_id); - } - break; - default: - LOG_ERROR("Unsupported Key Details: skipping"); - } + Lock lock(Lock::Type::UNSUPPORTED); + Private::buildLock(lock, *recipient); + priv->locks.push_back(std::move(lock)); } } diff --git a/cdoc/Crypto.cpp b/cdoc/Crypto.cpp index 81ac0c3c..9f902ba5 100644 --- a/cdoc/Crypto.cpp +++ b/cdoc/Crypto.cpp @@ -621,6 +621,6 @@ result_t DecryptionSource::close() int len = 0; std::vector buffer(EVP_CIPHER_CTX_block_size(ctx.get()), 0); if (SSL_FAILED(EVP_CipherFinal_ex(ctx.get(), buffer.data(), &len), "EVP_CipherFinal_ex")) - return error = CRYPTO_ERROR; + return error = HASH_MISMATCH; return OK; } diff --git a/cdoc/Lock.h b/cdoc/Lock.h index 99d5f720..6c91e546 100644 --- a/cdoc/Lock.h +++ b/cdoc/Lock.h @@ -47,6 +47,11 @@ struct CDOC_EXPORT Lock * @brief Invalid value */ INVALID, + /** + * @brief Valid capsule but not supported by this library version + * + */ + UNSUPPORTED, /** * @brief Symmetric AES key */ @@ -194,7 +199,7 @@ struct CDOC_EXPORT Lock * @brief check whether lock is valid * @return true if valid */ - bool isValid() const noexcept { return (type != Type::INVALID) && !label.empty() && !encrypted_fmk.empty(); } + bool isValid() const noexcept { return (type != Type::INVALID) && (type != Type::UNSUPPORTED) && !label.empty() && !encrypted_fmk.empty(); } /** * @brief check whether lock is based on symmetric key * @return true if type is SYMMETRIC_KEY or PASSWORD diff --git a/test/libcdoc_boost.cpp b/test/libcdoc_boost.cpp index 3b48536e..1401dc39 100644 --- a/test/libcdoc_boost.cpp +++ b/test/libcdoc_boost.cpp @@ -489,7 +489,7 @@ BOOST_FIXTURE_TEST_CASE_WITH_DECOR(CDoc2DecryptErrors, DecryptFixture, BOOST_TEST(rdr->beginDecryption(fmk) == libcdoc::OK); BOOST_TEST(rdr->nextFile(fi) == libcdoc::OK); BOOST_TEST(rdr->nextFile(fi) == libcdoc::OK); - BOOST_TEST(rdr->finishDecryption() == libcdoc::CRYPTO_ERROR); + BOOST_TEST(rdr->finishDecryption() == libcdoc::HASH_MISMATCH); delete rdr; // Truncate file, should result zlib error @@ -499,15 +499,15 @@ BOOST_FIXTURE_TEST_CASE_WITH_DECOR(CDoc2DecryptErrors, DecryptFixture, BOOST_TEST(rdr->getFMK(fmk, 0) == libcdoc::OK); BOOST_TEST(rdr->beginDecryption(fmk) == libcdoc::OK); libcdoc::result_t rv = rdr->nextFile(fi); - BOOST_TEST(((rv == libcdoc::OK) || (rv == libcdoc::CRYPTO_ERROR))); + BOOST_TEST(((rv == libcdoc::OK) || (rv == libcdoc::HASH_MISMATCH))); for (int i = 0; i < 4; i++) { rv = rdr->readData(buf, 256); - BOOST_TEST(((rv == 256) || (rv == libcdoc::CRYPTO_ERROR))); + BOOST_TEST(((rv == 256) || (rv == libcdoc::HASH_MISMATCH))); } rv = rdr->nextFile(fi); - BOOST_TEST(((rv == libcdoc::OK) || (rv == libcdoc::CRYPTO_ERROR))); + BOOST_TEST(((rv == libcdoc::OK) || (rv == libcdoc::HASH_MISMATCH))); rv = rdr->readData(buf, 256); - BOOST_TEST(((rv == 255) || (rv == libcdoc::CRYPTO_ERROR))); + BOOST_TEST(((rv == 255) || (rv == libcdoc::HASH_MISMATCH))); BOOST_TEST(rdr->finishDecryption() == libcdoc::WORKFLOW_ERROR); delete rdr; } From 414452e0c185903ec543fc82ee7c53b617175a25 Mon Sep 17 00:00:00 2001 From: lauris71 Date: Mon, 2 Feb 2026 12:25:56 +0200 Subject: [PATCH 03/16] Update cdoc/CDoc2Reader.cpp Co-authored-by: Raul Metsma --- cdoc/CDoc2Reader.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cdoc/CDoc2Reader.cpp b/cdoc/CDoc2Reader.cpp index 91de38e7..41b3c3a2 100644 --- a/cdoc/CDoc2Reader.cpp +++ b/cdoc/CDoc2Reader.cpp @@ -686,9 +686,7 @@ CDoc2Reader::CDoc2Reader(libcdoc::DataSource *src, bool take_ownership) setLastError({}); for(const auto *recipient: *recipients){ - Lock lock(Lock::Type::UNSUPPORTED); - Private::buildLock(lock, *recipient); - priv->locks.push_back(std::move(lock)); + Private::buildLock(priv->locks.emplace_back(Lock::Type::UNSUPPORTED), *recipient); } } From 0854344b75d815a300eb41fd0cfd0f4176b5bb50 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Mon, 2 Feb 2026 13:00:09 +0200 Subject: [PATCH 04/16] Use toUint8Vector helper --- cdoc/CDoc2Reader.cpp | 26 +++++++++++++------------- cdoc/CryptoBackend.h | 4 ++-- cdoc/NetworkBackend.cpp | 5 ++--- cdoc/Utils.h | 10 ++++++++++ 4 files changed, 27 insertions(+), 18 deletions(-) diff --git a/cdoc/CDoc2Reader.cpp b/cdoc/CDoc2Reader.cpp index 41b3c3a2..514ee251 100644 --- a/cdoc/CDoc2Reader.cpp +++ b/cdoc/CDoc2Reader.cpp @@ -215,7 +215,7 @@ CDoc2Reader::getFMK(std::vector& fmk, unsigned int lock_idx) } } else { std::vector kek_pm; - int result = crypto->deriveHMACExtract(kek_pm, key_material, std::vector(libcdoc::CDoc2::KEKPREMASTER.cbegin(), libcdoc::CDoc2::KEKPREMASTER.cend()), lock_idx); + int result = crypto->deriveHMACExtract(kek_pm, key_material, toUint8Vector(libcdoc::CDoc2::KEKPREMASTER), lock_idx); if (result < 0) { setLastError(crypto->getLastErrorStr(result)); LOG_ERROR("{}", last_error); @@ -411,7 +411,7 @@ CDoc2Reader::beginDecryption(const std::vector& fmk) LOG_TRACE_KEY("cek: {}", cek); priv->dec = std::make_unique(*priv->_src, EVP_chacha20_poly1305(), cek, libcdoc::CDoc2::NONCE_LEN); - std::vector aad(libcdoc::CDoc2::PAYLOAD.cbegin(), libcdoc::CDoc2::PAYLOAD.cend()); + std::vector aad = toUint8Vector(libcdoc::CDoc2::PAYLOAD); aad.insert(aad.end(), priv->header_data.cbegin(), priv->header_data.cend()); aad.insert(aad.end(), priv->headerHMAC.cbegin(), priv->headerHMAC.cend()); if(auto rv = priv->dec->updateAAD(aad); rv != OK) { @@ -496,7 +496,7 @@ CDoc2Reader::Private::buildLock(Lock& lock, const cdoc20::header::RecipientRecor using namespace cdoc20::header; lock.label = recipient.key_label()->str(); - lock.encrypted_fmk.assign(recipient.encrypted_fmk()->cbegin(), recipient.encrypted_fmk()->cend()); + lock.encrypted_fmk = toUint8Vector(recipient.encrypted_fmk()); if(recipient.fmk_encryption_method() != cdoc20::header::FMKEncryptionMethod::XOR) { @@ -510,8 +510,8 @@ CDoc2Reader::Private::buildLock(Lock& lock, const cdoc20::header::RecipientRecor if(key->curve() == EllipticCurve::secp384r1) { lock.type = Lock::Type::PUBLIC_KEY; lock.pk_type = Lock::PKType::ECC; - lock.setBytes(Lock::Params::RCPT_KEY, std::vector(key->recipient_public_key()->cbegin(), key->recipient_public_key()->cend())); - lock.setBytes(Lock::Params::KEY_MATERIAL, std::vector(key->sender_public_key()->cbegin(), key->sender_public_key()->cend())); + lock.setBytes(Lock::Params::RCPT_KEY, toUint8Vector(key->recipient_public_key())); + lock.setBytes(Lock::Params::KEY_MATERIAL, toUint8Vector(key->sender_public_key())); LOG_DBG("Load PK: {}", toHex(lock.getBytes(Lock::Params::RCPT_KEY))); } else { LOG_ERROR("Unsupported ECC curve: skipping"); @@ -523,8 +523,8 @@ CDoc2Reader::Private::buildLock(Lock& lock, const cdoc20::header::RecipientRecor { lock.type = Lock::Type::PUBLIC_KEY; lock.pk_type = Lock::PKType::RSA; - lock.setBytes(Lock::Params::RCPT_KEY, std::vector(key->recipient_public_key()->cbegin(), key->recipient_public_key()->cend())); - lock.setBytes(Lock::Params::KEY_MATERIAL, std::vector(key->encrypted_kek()->cbegin(), key->encrypted_kek()->cend())); + lock.setBytes(Lock::Params::RCPT_KEY, toUint8Vector(key->recipient_public_key())); + lock.setBytes(Lock::Params::KEY_MATERIAL, toUint8Vector(key->encrypted_kek())); } return; case Capsule::recipients_KeyServerCapsule: @@ -538,7 +538,7 @@ CDoc2Reader::Private::buildLock(Lock& lock, const cdoc20::header::RecipientRecor return; } lock.pk_type = Lock::PKType::ECC; - lock.setBytes(Lock::Params::RCPT_KEY, std::vector(eccDetails->recipient_public_key()->cbegin(), eccDetails->recipient_public_key()->cend())); + lock.setBytes(Lock::Params::RCPT_KEY, toUint8Vector(eccDetails->recipient_public_key())); } else { LOG_ERROR("Invalid file format"); return; @@ -547,7 +547,7 @@ CDoc2Reader::Private::buildLock(Lock& lock, const cdoc20::header::RecipientRecor case KeyDetailsUnion::RsaKeyDetails: if(const RsaKeyDetails *rsaDetails = server->recipient_key_details_as_RsaKeyDetails()) { lock.pk_type = Lock::PKType::RSA; - lock.setBytes(Lock::Params::RCPT_KEY, std::vector(rsaDetails->recipient_public_key()->cbegin(), rsaDetails->recipient_public_key()->cend())); + lock.setBytes(Lock::Params::RCPT_KEY, toUint8Vector(rsaDetails->recipient_public_key())); } else { LOG_ERROR("Invalid file format"); return; @@ -568,7 +568,7 @@ CDoc2Reader::Private::buildLock(Lock& lock, const cdoc20::header::RecipientRecor if(const auto *capsule = recipient.capsule_as_recipients_SymmetricKeyCapsule()) { lock.type = Lock::SYMMETRIC_KEY; - lock.setBytes(Lock::SALT, std::vector(capsule->salt()->cbegin(), capsule->salt()->cend())); + lock.setBytes(Lock::SALT, toUint8Vector(capsule->salt())); } return; case Capsule::recipients_PBKDF2Capsule: @@ -579,8 +579,8 @@ CDoc2Reader::Private::buildLock(Lock& lock, const cdoc20::header::RecipientRecor return; } lock.type = Lock::PASSWORD; - lock.setBytes(Lock::SALT, std::vector(capsule->salt()->cbegin(), capsule->salt()->cend())); - lock.setBytes(Lock::PW_SALT, std::vector(capsule->password_salt()->cbegin(), capsule->password_salt()->cend())); + lock.setBytes(Lock::SALT, toUint8Vector(capsule->salt())); + lock.setBytes(Lock::PW_SALT, toUint8Vector(capsule->password_salt())); lock.setInt(Lock::KDF_ITER, capsule->kdf_iterations()); } return; @@ -605,7 +605,7 @@ CDoc2Reader::Private::buildLock(Lock& lock, const cdoc20::header::RecipientRecor } std::string urls = join(strs, ";"); LOG_DBG("Keyshare urls: {}", urls); - std::vector salt(capsule->salt()->cbegin(), capsule->salt()->cend()); + std::vector salt = toUint8Vector(capsule->salt()); LOG_DBG("Keyshare salt: {}", toHex(salt)); std::string recipient_id = capsule->recipient_id()->str(); LOG_DBG("Keyshare recipient id: {}", recipient_id); diff --git a/cdoc/CryptoBackend.h b/cdoc/CryptoBackend.h index c1013c0e..56e64475 100644 --- a/cdoc/CryptoBackend.h +++ b/cdoc/CryptoBackend.h @@ -36,8 +36,8 @@ struct Lock; * - decryptRSA for RSA keys * - getSecret for symmetric keys. * - * ECC and symmetric keys have also frontend methods; implementing these allows the program to perform certain cryptographic procedures in controlled - * environment and (in case of symmetric keys) avoid exposing secret keys/passwords. + * ECC and symmetric keys have also frontend methods; implementing these allows the program to perform certain cryptographic procedures in secure + * environment and (in case of symmetric keys) avoid exposing secret keys/passwords to library code. */ struct CDOC_EXPORT CryptoBackend { static constexpr int INVALID_PARAMS = -201; diff --git a/cdoc/NetworkBackend.cpp b/cdoc/NetworkBackend.cpp index 40163de2..3fb52d06 100644 --- a/cdoc/NetworkBackend.cpp +++ b/cdoc/NetworkBackend.cpp @@ -397,8 +397,7 @@ libcdoc::NetworkBackend::fetchKey (std::vector& dst, const std::string& } error = {}; std::string ks = v.get(); - std::vector key_material = fromBase64(ks); - dst.assign(key_material.cbegin(), key_material.cend()); + dst = fromBase64(ks); return libcdoc::OK; } @@ -434,7 +433,7 @@ libcdoc::NetworkBackend::fetchNonce(std::vector& dst, const std::string return NETWORK_ERROR; } std::string nonce_str = v.get(); - dst.assign(nonce_str.cbegin(), nonce_str.cend()); + dst = toUint8Vector(nonce_str); return OK; } diff --git a/cdoc/Utils.h b/cdoc/Utils.h index a0283894..a98e4c83 100644 --- a/cdoc/Utils.h +++ b/cdoc/Utils.h @@ -125,6 +125,16 @@ struct urlEncode { friend std::ostream& operator<<(std::ostream& escaped, urlEncode src); }; +std::vector toUint8Vector(const auto* data) +{ + return {data->cbegin(), data->cend()}; +} + +std::vector toUint8Vector(const auto& data) +{ + return {data.cbegin(), data.cend()}; +} + std::string urlDecode(const std::string &src); } // namespace libcdoc From 777700cc6b799ca2daa90296ec7716f31b4c5d56 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Mon, 2 Feb 2026 17:15:55 +0200 Subject: [PATCH 05/16] Removed some extra check from parsing --- cdoc/CDoc2Reader.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/cdoc/CDoc2Reader.cpp b/cdoc/CDoc2Reader.cpp index 514ee251..30cf6831 100644 --- a/cdoc/CDoc2Reader.cpp +++ b/cdoc/CDoc2Reader.cpp @@ -539,29 +539,21 @@ CDoc2Reader::Private::buildLock(Lock& lock, const cdoc20::header::RecipientRecor } lock.pk_type = Lock::PKType::ECC; lock.setBytes(Lock::Params::RCPT_KEY, toUint8Vector(eccDetails->recipient_public_key())); - } else { - LOG_ERROR("Invalid file format"); - return; } break; case KeyDetailsUnion::RsaKeyDetails: if(const RsaKeyDetails *rsaDetails = server->recipient_key_details_as_RsaKeyDetails()) { lock.pk_type = Lock::PKType::RSA; lock.setBytes(Lock::Params::RCPT_KEY, toUint8Vector(rsaDetails->recipient_public_key())); - } else { - LOG_ERROR("Invalid file format"); - return; } break; default: - LOG_ERROR("Unsupported Key Server Details: skipping"); + LOG_ERROR("Unsupported Key Server Details"); return; } lock.type = Lock::Type::SERVER; lock.setString(Lock::Params::KEYSERVER_ID, server->keyserver_id()->str()); lock.setString(Lock::Params::TRANSACTION_ID, server->transaction_id()->str()); - } else { - LOG_ERROR("Invalid file format"); } return; case Capsule::recipients_SymmetricKeyCapsule: @@ -616,7 +608,7 @@ CDoc2Reader::Private::buildLock(Lock& lock, const cdoc20::header::RecipientRecor } return; default: - LOG_ERROR("Unsupported Key Details: skipping"); + LOG_ERROR("Unsupported capsule type"); } } From 8cdc65201185d1aac1680f073fdd7be9202403d9 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Tue, 3 Feb 2026 15:01:48 +0200 Subject: [PATCH 06/16] Remove INVALID lock type and rename UNSUPPORTED to UNKNOWN --- cdoc/CDoc2Reader.cpp | 2 +- cdoc/Lock.h | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/cdoc/CDoc2Reader.cpp b/cdoc/CDoc2Reader.cpp index 30cf6831..913adca3 100644 --- a/cdoc/CDoc2Reader.cpp +++ b/cdoc/CDoc2Reader.cpp @@ -678,7 +678,7 @@ CDoc2Reader::CDoc2Reader(libcdoc::DataSource *src, bool take_ownership) setLastError({}); for(const auto *recipient: *recipients){ - Private::buildLock(priv->locks.emplace_back(Lock::Type::UNSUPPORTED), *recipient); + Private::buildLock(priv->locks.emplace_back(), *recipient); } } diff --git a/cdoc/Lock.h b/cdoc/Lock.h index 6c91e546..3011f871 100644 --- a/cdoc/Lock.h +++ b/cdoc/Lock.h @@ -43,15 +43,11 @@ struct CDOC_EXPORT Lock * @brief The lock type */ enum Type : unsigned char { - /** - * @brief Invalid value - */ - INVALID, /** * @brief Valid capsule but not supported by this library version * */ - UNSUPPORTED, + UNKNOWN, /** * @brief Symmetric AES key */ @@ -180,7 +176,7 @@ struct CDOC_EXPORT Lock /** * @brief The lock type */ - Type type = Type::INVALID; + Type type = Type::UNKNOWN; /** * @brief algorithm type for public key based locks */ @@ -199,7 +195,7 @@ struct CDOC_EXPORT Lock * @brief check whether lock is valid * @return true if valid */ - bool isValid() const noexcept { return (type != Type::INVALID) && (type != Type::UNSUPPORTED) && !label.empty() && !encrypted_fmk.empty(); } + bool isValid() const noexcept { return (type != Type::UNKNOWN) && !label.empty() && !encrypted_fmk.empty(); } /** * @brief check whether lock is based on symmetric key * @return true if type is SYMMETRIC_KEY or PASSWORD From 5ef36d7c11098c3cbeb4becbcc34c6360093e3f3 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Wed, 4 Feb 2026 13:11:42 +0200 Subject: [PATCH 07/16] Return NULL from frontend if CDoc constructor fails --- cdoc/CDoc.cpp | 51 +++++++++++--------------------------------- cdoc/CDoc2Reader.cpp | 2 +- cdoc/Io.h | 2 -- 3 files changed, 14 insertions(+), 41 deletions(-) diff --git a/cdoc/CDoc.cpp b/cdoc/CDoc.cpp index 97e393b0..0be43f8b 100644 --- a/cdoc/CDoc.cpp +++ b/cdoc/CDoc.cpp @@ -103,7 +103,7 @@ libcdoc::CDocReader::createReader(DataSource *src, bool take_ownership, Configur int version = getCDocFileVersion(src); LOG_DBG("CDocReader::createReader: version {}", version); if (src->seek(0) != libcdoc::OK) return nullptr; - CDocReader *reader; + CDocReader *reader = nullptr; if (version == 1) { reader = new CDoc1Reader(src, take_ownership); } else if (version == 2) { @@ -111,12 +111,16 @@ libcdoc::CDocReader::createReader(DataSource *src, bool take_ownership, Configur } else { if(take_ownership) delete src; - return nullptr; - } - reader->conf = conf; - reader->crypto = crypto; + return nullptr; + } + if (!reader->getLastErrorStr().empty()) { + delete reader; + return nullptr; + } + reader->conf = conf; + reader->crypto = crypto; reader->network = network; - return reader; + return reader; } libcdoc::CDocReader * @@ -125,43 +129,14 @@ libcdoc::CDocReader::createReader(const std::string& path, Configuration *conf, if(path.empty()) return nullptr; auto isrc = make_unique(path); - int version = getCDocFileVersion(isrc.get()); - LOG_DBG("CDocReader::createReader: version {}", version); - if (isrc->seek(0) != libcdoc::OK) - return nullptr; - CDocReader *reader; - if (version == 1) { - reader = new CDoc1Reader(isrc.release(), true); - } else if (version == 2) { - reader = new CDoc2Reader(isrc.release(), true); - } else { - return nullptr; - } - reader->conf = conf; - reader->crypto = crypto; - reader->network = network; - return reader; + return createReader(isrc.release(), true, conf, crypto, network); } libcdoc::CDocReader * libcdoc::CDocReader::createReader(std::istream& ifs, Configuration *conf, CryptoBackend *crypto, NetworkBackend *network) { - libcdoc::IStreamSource *isrc = new libcdoc::IStreamSource(&ifs, false); - int version = getCDocFileVersion(isrc); - LOG_DBG("CDocReader::createReader: version {}", version); - CDocReader *reader; - if (version == 1) { - reader = new CDoc1Reader(isrc, true); - } else if (version == 2) { - reader = new CDoc2Reader(isrc, true); - } else { - delete isrc; - return nullptr; - } - reader->conf = conf; - reader->crypto = crypto; - reader->network = network; - return reader; + auto isrc = make_unique(&ifs, false); + return createReader(isrc.release(), true, conf, crypto, network); } #if LIBCDOC_TESTING diff --git a/cdoc/CDoc2Reader.cpp b/cdoc/CDoc2Reader.cpp index 913adca3..6b83dbc9 100644 --- a/cdoc/CDoc2Reader.cpp +++ b/cdoc/CDoc2Reader.cpp @@ -588,7 +588,7 @@ CDoc2Reader::Private::buildLock(Lock& lock, const cdoc20::header::RecipientRecor } /* url,share_id;url,share_id... */ std::vector strs; - for (auto cshare = capsule->shares()->cbegin(); cshare != capsule->shares()->cend(); ++cshare) { + for (auto cshare : *capsule->shares()) { std::string id = cshare->share_id()->str(); std::string url = cshare->server_base_url()->str(); std::string str = url + "," + id; diff --git a/cdoc/Io.h b/cdoc/Io.h index 45902101..dfc04199 100644 --- a/cdoc/Io.h +++ b/cdoc/Io.h @@ -259,8 +259,6 @@ struct CDOC_EXPORT IStreamSource : public DataSource { if(_ifs->bad()) return INPUT_STREAM_ERROR; _ifs->clear(); _ifs->seekg(pos); - //std::cerr << "Stream bad:" << _ifs->bad() << " eof:" << _ifs->eof() << " fail:" << _ifs->fail() << std::endl; - //std::cerr << "tell:" << _ifs->tellg() << std::endl; return bool(_ifs->bad()) ? INPUT_STREAM_ERROR : OK; } From 625c76826fc4f0ca33affda006dfadda5656ea90 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Wed, 11 Feb 2026 10:48:03 +0200 Subject: [PATCH 08/16] Remove label creation from Java test --- examples/java/src/main/java/ee/ria/cdoc/CDocTool.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/java/src/main/java/ee/ria/cdoc/CDocTool.java b/examples/java/src/main/java/ee/ria/cdoc/CDocTool.java index edfcd382..fabc9d02 100644 --- a/examples/java/src/main/java/ee/ria/cdoc/CDocTool.java +++ b/examples/java/src/main/java/ee/ria/cdoc/CDocTool.java @@ -363,8 +363,7 @@ static void locks(String path) { static void test() { System.err.println("Testing label generation"); - String label = Recipient.buildLabel(new String[] {"Alpha", "1", "Beta", "2", "Gamma", "3", "Delta"}); - System.err.format("Label: %s\n", label); + String label = "data:v=1&type=ID-card&serial_number=PNOEE-38001085718&cn=J%C3%95EORG%2CJAAK-KRISTJAN%2C38001085718"; java.util.Map map = Recipient.parseLabel(label); for (String key : map.keySet()) { System.err.format(" %s:%s\n", key, map.get(key)); From 372f7f7842982d8dc0ddf81b2ab256c5c162f0a4 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Thu, 12 Feb 2026 13:40:54 +0200 Subject: [PATCH 09/16] Keep Java references in CDocReader and CDocWriter to prevent premature GC --- libcdoc.i | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/libcdoc.i b/libcdoc.i index 3b65a7cb..7e800a76 100644 --- a/libcdoc.i +++ b/libcdoc.i @@ -103,7 +103,7 @@ // CDocReader // -// Custom wrapper do away with const qualifiers +// Custom wrapper to do away with const qualifiers %extend libcdoc::CDocReader { std::vector getLocks() { const std::vector &locks = $self->getLocks(); @@ -505,7 +505,14 @@ static std::vector SWIG_JavaArrayToVectorUnsignedChar(JNIEnv *jen %typemap(javadirectorin) std::string_view "$jniinput" // No return of std::string_view so no javadirectorout +// CDocReader + %typemap(javacode) libcdoc::CDocReader %{ + // Keep Java references to prevent GC deleting these prematurely + private Configuration config; + private CryptoBackend crypto; + private NetworkBackend network; + public void readFile(java.io.OutputStream ofs) throws CDocException, java.io.IOException { byte[] buf = new byte[1024]; long result = readData(buf); @@ -516,6 +523,37 @@ static std::vector SWIG_JavaArrayToVectorUnsignedChar(JNIEnv *jen } %} +%typemap(javaout) libcdoc::CDocReader * libcdoc::CDocReader::createReader { + long cPtr = $jnicall; + if (cPtr == 0) return null; + CDocReader rdr = new CDocReader(cPtr, true); + // Set Java references + rdr.config = conf; + rdr.crypto = crypto; + rdr.network = network; + return rdr; +} + +// CDocWriter + +%typemap(javacode) libcdoc::CDocWriter %{ + // Keep Java references to prevent GC deleting these prematurely + private Configuration config; + private CryptoBackend crypto; + private NetworkBackend network; +%} + +%typemap(javaout) libcdoc::CDocWriter * libcdoc::CDocWriter::createWriter { + long cPtr = $jnicall; + if (cPtr == 0) return null; + CDocWriter wrtr = new CDocWriter(cPtr, true); + // Set Java references + wrtr.config = conf; + wrtr.crypto = crypto; + wrtr.network = network; + return wrtr; +} + %typemap(javacode) libcdoc::Configuration %{ public static final String KEYSERVER_SEND_URL = "KEYSERVER_SEND_URL"; public static final String KEYSERVER_FETCH_URL = "KEYSERVER_FETCH_URL"; From 4dc229f73929d4f839c9218fa864ef204e027ac4 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 13 Feb 2026 13:21:16 +0200 Subject: [PATCH 10/16] Cleaned up logging --- cdoc/CDoc.cpp | 24 +++- cdoc/CDoc.h | 44 +++++++ cdoc/CMakeLists.txt | 1 - cdoc/ConsoleLogger.h | 9 +- cdoc/Crypto.cpp | 2 +- cdoc/ILogger.h | 119 +++++------------- cdoc/LogEngine.cpp | 110 ---------------- cdoc/cdoc-tool.cpp | 6 +- .../src/main/java/ee/ria/cdoc/CDocTool.java | 110 ++++++++++++++-- 9 files changed, 207 insertions(+), 218 deletions(-) delete mode 100644 cdoc/LogEngine.cpp diff --git a/cdoc/CDoc.cpp b/cdoc/CDoc.cpp index 0be43f8b..37cfb832 100644 --- a/cdoc/CDoc.cpp +++ b/cdoc/CDoc.cpp @@ -21,7 +21,7 @@ #include "CDoc2Writer.h" #include "CDoc2Reader.h" #include "Configuration.h" -#include "ILogger.h" +#include "ConsoleLogger.h" #include "Io.h" #include "NetworkBackend.h" @@ -71,6 +71,28 @@ getVersion() return VERSION_STR; } +static Logger * +getDefaultLogger() +{ + static ConsoleLogger clogger; + return &clogger; +} + +static Logger *sys_logger = nullptr; + +void +setLogger(Logger *logger) +{ + sys_logger = logger; +} + +void +log(LogLevel level, std::string_view file, int line, std::string_view msg) +{ + Logger *logger = (sys_logger) ? sys_logger : getDefaultLogger(); + logger->log(level, file, line, msg); +} + int libcdoc::CDocReader::getCDocFileVersion(DataSource *src) { diff --git a/cdoc/CDoc.h b/cdoc/CDoc.h index 7bad17c7..4b048e36 100644 --- a/cdoc/CDoc.h +++ b/cdoc/CDoc.h @@ -134,6 +134,50 @@ CDOC_EXPORT std::string getErrorStr(int64_t code); CDOC_EXPORT std::string getVersion(); +// Logging interface + +class ILogger; +typedef ILogger Logger; + +/** + * @brief Log-level enumeration to indicate severity of the log message. + */ +enum LogLevel +{ + /** + * @brief Most critical level. Application is about to abort. + */ + LEVEL_FATAL, + + /** + * @brief Errors where functionality has failed or an exception have been caught. + */ + LEVEL_ERROR, + + /** + * @brief Warnings about validation issues or temporary failures that can be recovered. + */ + LEVEL_WARNING, + + /** + * @brief Information that highlights progress or application lifetime events. + */ + LEVEL_INFO, + + /** + * @brief Debugging the application behavior from internal events of interest. + */ + LEVEL_DEBUG, + + /** + * @brief Most verbose level. Used for development, NOP in production code. + */ + LEVEL_TRACE +}; + +CDOC_EXPORT void setLogger(Logger *logger); +CDOC_EXPORT void log(LogLevel level, std::string_view file, int line, std::string_view msg); + /** * @brief A simple container of file name and size * diff --git a/cdoc/CMakeLists.txt b/cdoc/CMakeLists.txt index 97319077..f55a826a 100644 --- a/cdoc/CMakeLists.txt +++ b/cdoc/CMakeLists.txt @@ -38,7 +38,6 @@ add_library(cdoc CryptoBackend.cpp NetworkBackend.cpp PKCS11Backend.cpp - LogEngine.cpp $<$:WinBackend.cpp> Certificate.cpp Certificate.h Crypto.cpp Crypto.h diff --git a/cdoc/ConsoleLogger.h b/cdoc/ConsoleLogger.h index 06061d7f..c1302b8f 100644 --- a/cdoc/ConsoleLogger.h +++ b/cdoc/ConsoleLogger.h @@ -35,14 +35,11 @@ namespace libcdoc class ConsoleLogger : public ILogger { public: - virtual void LogMessage(LogLevel level, std::string_view file, int line, std::string_view message) override + virtual void logMessage(LogLevel level, std::string_view file, int line, std::string_view message) override { // We ignore by default the file name and line number, and call LogMessage with the level and message. - if (level <= minLogLevel) - { - std::ostream& ofs = (level == LEVEL_INFO) ? std::cout : std::cerr; - ofs << message << '\n'; - } + std::ostream& ofs = (level == LEVEL_INFO) ? std::cout : std::cerr; + ofs << message << '\n'; } }; diff --git a/cdoc/Crypto.cpp b/cdoc/Crypto.cpp index 9f902ba5..bf796ef1 100644 --- a/cdoc/Crypto.cpp +++ b/cdoc/Crypto.cpp @@ -426,7 +426,7 @@ void Crypto::LogSslError(const char* funcName, const char* file, int line) while (errorCode != 0) { ERR_error_string_n(errorCode, sslErrorStr, errorStrBufLen); - ILogger::getLogger()->LogMessage(ILogger::LEVEL_ERROR, file, line, FORMAT("{} failed: {}", funcName, sslErrorStr)); + LOG_ERROR("{} failed: {}", funcName, sslErrorStr); // Get next error code errorCode = ERR_get_error(); diff --git a/cdoc/ILogger.h b/cdoc/ILogger.h index 3bf1c72c..e66acc81 100644 --- a/cdoc/ILogger.h +++ b/cdoc/ILogger.h @@ -31,6 +31,8 @@ namespace fmt = std; #include "fmt/format.h" #endif +#include + #define FORMAT fmt::format namespace libcdoc @@ -42,62 +44,19 @@ namespace libcdoc class CDOC_EXPORT ILogger { public: - /** - * @brief Log-level enumeration to indicate severity of the log message. - */ - enum LogLevel - { - /** - * @brief Most critical level. Application is about to abort. - */ - LEVEL_FATAL, - - /** - * @brief Errors where functionality has failed or an exception have been caught. - */ - LEVEL_ERROR, - - /** - * @brief Warnings about validation issues or temporary failures that can be recovered. - */ - LEVEL_WARNING, - - /** - * @brief Information that highlights progress or application lifetime events. - */ - LEVEL_INFO, - - /** - * @brief Debugging the application behavior from internal events of interest. - */ - LEVEL_DEBUG, - - /** - * @brief Most verbose level. Used for development, NOP in production code. - */ - LEVEL_TRACE - }; - - ILogger() : minLogLevel(LEVEL_WARNING) {} - virtual ~ILogger() {} - /** * @brief Logs given message with given severity, file name and line number. + * + * It tests the log level and if <= min_level invokes logMessage + * * @param level Severity of the log message. * @param file File name where the log message was recorded. * @param line Line number in the file where the log message was recorded. - * @param message The log message. - * - * Every class implementing the ILogger interface must implement the member function. - * Default implementation does nothing. + * @param msg The log message. */ - virtual void LogMessage(LogLevel level, std::string_view file, int line, std::string_view message) {} - - /** - * @brief Returns current minimum log level of the logger. - * @return Minimum log level. - */ - LogLevel GetMinLogLevel() const noexcept { return minLogLevel; } + void log(LogLevel level, std::string_view file, int line, std::string_view msg) { + if (level <= min_level) logMessage(level, file, line, msg); + } /** * @brief Sets minimum log level for the logger. @@ -107,68 +66,56 @@ class CDOC_EXPORT ILogger * to LogLevelInfo (default), then LogLevelFatal, LogLevelError, LogLevelWarning and LogLevelInfo * messages are logged, but not LogLevelDebug or LogLevelTrace messages. */ - void SetMinLogLevel(LogLevel level) noexcept { minLogLevel = level; } - + void setMinLogLevel(LogLevel level) noexcept { min_level = level; } +protected: /** - * @brief Adds ILogger implementation to logging queue. + * @brief Logs given message with given severity, file name and line number. * - * This function does not take ownership of the logger's instance. - * It is up to the caller to free the resources of the logger's instance and - * keep it alive until removed from the queue. + * Every class implementing the ILogger interface must implement this member function. + * The efault implementation does nothing. + * The level should be checked by caller, thus the implementation should expect that level <= min_level * - * @param logger Logger's instance to be added. - * @return Unique cookie identifying the logger's instance in the logging queue. - */ - static int addLogger(ILogger* logger); - - /** - * @brief Removes logger's instance from the logging queue. - * @param cookie Unique cookie returned by the add_logger function when the logger was added. - * @return Pointer to ILogger object that is removed. It's up to user to free the resources. - */ - static ILogger* removeLogger(int cookie); - - /** - * @brief Returns global logger's instance. - * @return Global logger's instance. + * @param level Severity of the log message. + * @param file File name where the log message was recorded. + * @param line Line number in the file where the log message was recorded. + * @param msg The log message. */ - static ILogger* getLogger(); + virtual void logMessage(LogLevel level, std::string_view file, int line, std::string_view msg) {} - static void setLogger(ILogger *logger); - -protected: /** * @brief Minimum level of log messages to log. */ - LogLevel minLogLevel; + LogLevel min_level = LEVEL_WARNING; }; +typedef ILogger Logger; + #ifndef SWIG template -static inline void LogFormat(ILogger::LogLevel level, std::string_view file, int line, fmt::format_string fmt, Args&&... args) +static inline void LogFormat(LogLevel level, std::string_view file, int line, fmt::format_string fmt, Args&&... args) { auto msg = fmt::format(fmt, std::forward(args)...); - ILogger::getLogger()->LogMessage(level, file, line, msg); + libcdoc::log(level, file, line, msg); } -static inline void LogFormat(ILogger::LogLevel level, std::string_view file, int line, std::string_view msg) +static inline void LogFormat(LogLevel level, std::string_view file, int line, std::string_view msg) { - ILogger::getLogger()->LogMessage(level, file, line, msg); + libcdoc::log(level, file, line, msg); } #endif #define LOG(l,...) LogFormat((l), __FILE__, __LINE__, __VA_ARGS__) -#define LOG_ERROR(...) LogFormat(libcdoc::ILogger::LEVEL_ERROR, __FILE__, __LINE__, __VA_ARGS__) -#define LOG_WARN(...) LogFormat(libcdoc::ILogger::LEVEL_WARNING, __FILE__, __LINE__, __VA_ARGS__) -#define LOG_INFO(...) LogFormat(libcdoc::ILogger::LEVEL_INFO, __FILE__, __LINE__, __VA_ARGS__) -#define LOG_DBG(...) LogFormat(libcdoc::ILogger::LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_ERROR(...) LogFormat(libcdoc::LEVEL_ERROR, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_WARN(...) LogFormat(libcdoc::LEVEL_WARNING, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_INFO(...) LogFormat(libcdoc::LEVEL_INFO, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_DBG(...) LogFormat(libcdoc::LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__) #ifdef NDEBUG #define LOG_TRACE(...) #define LOG_TRACE_KEY(MSG, KEY) #else -#define LOG_TRACE(...) LogFormat(libcdoc::ILogger::LEVEL_TRACE, __FILE__, __LINE__, __VA_ARGS__) -#define LOG_TRACE_KEY(MSG, KEY) LogFormat(libcdoc::ILogger::LEVEL_TRACE, __FILE__, __LINE__, MSG, toHex(KEY)) +#define LOG_TRACE(...) LogFormat(libcdoc::LEVEL_TRACE, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_TRACE_KEY(MSG, KEY) LogFormat(libcdoc::LEVEL_TRACE, __FILE__, __LINE__, MSG, toHex(KEY)) #endif } diff --git a/cdoc/LogEngine.cpp b/cdoc/LogEngine.cpp deleted file mode 100644 index e6bc69cc..00000000 --- a/cdoc/LogEngine.cpp +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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 "ILogger.h" - -#include -#include - -using namespace std; - -namespace libcdoc -{ -/** - * @brief Logging Engine implementation. - * - * The Logging Engine holds all instances of registered loggers and - * logs a log message to all the instances. - */ -struct LogEngine final : public ILogger -{ - void LogMessage(libcdoc::ILogger::LogLevel level, std::string_view file, int line, std::string_view message) final - { - lock_guard guard(loggers_protector); - for (const auto &[_, logger] : loggers) - { - logger->LogMessage(level, file, line, message); - } - } - - int AddLogger(ILogger* logger) - { - lock_guard guard(loggers_protector); - loggers[++currentLoggerCookie] = logger; - return currentLoggerCookie; - } - - ILogger* RemoveLogger(int cookie) - { - lock_guard guard(loggers_protector); - ILogger* tmp = loggers[cookie]; - loggers.erase(cookie); - return tmp; - } - - void setLogger(ILogger *logger) { - lock_guard guard(loggers_protector); - while (!loggers.empty()) { - delete loggers.begin()->second; - loggers.erase(loggers.begin()->first); - } - loggers[0] = logger; - } - -private: - // Current Cookie value - int currentLoggerCookie = 0; - - // The map with registered loggers. - map loggers; - - // Loggers map concurrency protector - mutex loggers_protector; -}; - -// Default logger's instance - Logging Engine instance. -static LogEngine defaultLogEngine; - -// It is essential to define shared functions and variables with namespace. Otherwise, the linker won't find them. - -int -ILogger::addLogger(ILogger* logger) -{ - return defaultLogEngine.AddLogger(logger); -} - -ILogger* -ILogger::removeLogger(int cookie) -{ - return defaultLogEngine.RemoveLogger(cookie); -} - -ILogger* -ILogger::getLogger() -{ - return &defaultLogEngine; -} - -void -ILogger::setLogger(ILogger *logger) -{ - defaultLogEngine.setLogger(logger); -} - - -} diff --git a/cdoc/cdoc-tool.cpp b/cdoc/cdoc-tool.cpp index c00495fb..9cd8403c 100644 --- a/cdoc/cdoc-tool.cpp +++ b/cdoc/cdoc-tool.cpp @@ -653,8 +653,8 @@ int main(int argc, char *argv[]) // Add console logger by default ConsoleLogger console_logger; - console_logger.SetMinLogLevel(ILogger::LEVEL_TRACE); - int cookie = ILogger::addLogger(&console_logger); + console_logger.setMinLogLevel(LEVEL_TRACE); + libcdoc::setLogger(&console_logger); string_view command(argv[1]); LOG_INFO("Command: {}", command); @@ -678,6 +678,6 @@ int main(int argc, char *argv[]) print_usage(cout); } - ILogger::removeLogger(cookie); + setLogger(nullptr); return retVal; } diff --git a/examples/java/src/main/java/ee/ria/cdoc/CDocTool.java b/examples/java/src/main/java/ee/ria/cdoc/CDocTool.java index fabc9d02..837d7ca9 100644 --- a/examples/java/src/main/java/ee/ria/cdoc/CDocTool.java +++ b/examples/java/src/main/java/ee/ria/cdoc/CDocTool.java @@ -6,6 +6,7 @@ import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.HashMap; import java.util.Collection; @@ -33,11 +34,12 @@ public static String getArg(int arg_idx, String[] args) { } // Make logger static to ensure that it is not garbage-collected as long as it is atached to library - private static JavaLogger logger; + private static ILogger logger; public static void main(String[] args) { System.out.println("Starting app..."); + LogLevel log_level = LogLevel.LEVEL_INFO; String library = "../../build/macos/cdoc/libcdoc_javad.jnilib"; Action action = Action.INVALID; ArrayList files = new ArrayList<>(); @@ -119,6 +121,13 @@ public static void main(String[] args) { System.exit(1); } servers = args[i].split(","); + } else if (args[i].equals("--log-level")) { + i += 1; + if (i >= args.length) { + System.err.println("Invalid arguments"); + System.exit(1); + } + log_level = LogLevel.valueOf(args[i]); } else if (!args[i].startsWith("--")) { files.add(args[i]); } @@ -128,11 +137,11 @@ public static void main(String[] args) { System.load(lib.getAbsolutePath()); System.out.println("Library loaded"); - logger = new JavaLogger(); - //ConsoleLogger logger = new ConsoleLogger(); - logger.SetMinLogLevel(ILogger.LogLevel.LEVEL_TRACE); - ILogger.addLogger(logger); - ILogger.getLogger().LogMessage(ILogger.LogLevel.LEVEL_DEBUG, "FILENAME", 0, "Starting CDocTool.java"); + //logger = new JavaLogger(); + logger = new ConsoleLogger(); + logger.setMinLogLevel(log_level); + CDoc.setLogger(logger); + CDoc.log(LogLevel.LEVEL_DEBUG, "FILENAME", 0, "Starting CDocTool.java"); switch (action) { case ENCRYPT: @@ -373,15 +382,19 @@ static void test() { System.err.println("Creating ToolConf..."); ToolConf conf = new ToolConf(); + + System.err.println("Creating ToolCrypto"); + ToolCrypto crypto = new ToolCrypto(); + + System.err.println("Creating ToolNetwork"); + ToolNetwork network = new ToolNetwork(); + System.err.println("Creating DataBuffer..."); DataBuffer buf = new DataBuffer(); byte[] bytes = {1, 2, 3}; buf.setData(bytes); System.err.format("Buffer: %s\n", hex.formatHex(buf.getData())); - System.err.println("Creating ToolNetwork"); - ToolNetwork network = new ToolNetwork(); - System.err.println("Creating reader: " + path); CDocReader rdr = CDocReader.createReader(path, conf, null, network); System.err.format("Reader created (version %d)\n", rdr.getVersion()); @@ -397,9 +410,77 @@ static void test() { byte[] cert = certs.getCertificate(i); System.err.format(" %s\n", hex.formatHex(cert)); } + + testRW(conf, crypto); + System.err.println("Success"); } + static void testRW(ToolConf conf, ToolCrypto crypto) { + try { + final String container = "testfile.cdoc"; + final String password = "TereTalv!"; + final String label = "Jaanus Jõhvikas"; + + crypto.setPassword(password); + DataBuffer rnd = new DataBuffer(); + crypto.random(rnd, 57); + System.gc(); + final byte[] datain = rnd.getData(); + System.out.format("Data IN: %s\n", hex.formatHex(datain)); + + CDocWriter wrtr = CDocWriter.createWriter(2, container, conf, crypto, null); + System.gc(); + Recipient rcpt = Recipient.makeSymmetric(label, 65535); + long result = wrtr.addRecipient(rcpt); + System.gc(); + System.out.format("addRecipient: %d\n", result); + result = wrtr.beginEncryption(); + System.gc(); + System.out.format("beginEncryption: %d\n", result); + final String name = "kala"; + System.out.format("Adding file %s\n", name); + result = wrtr.addFile(name, datain.length); + System.gc(); + System.out.format("addFile: %d\n", result); + result = wrtr.writeData(datain); + System.gc(); + System.out.format("writeData: %d\n", result); + result = wrtr.finishEncryption(); + System.gc(); + System.out.format("finishEncryption: %d\n", result); + + CDocReader rdr = CDocReader.createReader(container, conf, crypto, null); + System.gc(); + System.out.format("Reader created (version %d)\n", rdr.getVersion()); + LockVector locks = rdr.getLocks(); + System.gc(); + ee.ria.cdoc.Lock lock = locks.get(0); + System.out.format("Lock: %s\n", lock.getLabel()); + byte[] fmk = rdr.getFMK(0); + System.gc(); + System.out.format("FMK is: %s\n", hex.formatHex(fmk)); + rdr.beginDecryption(fmk); + System.gc(); + FileInfo fi = new FileInfo(); + result = rdr.nextFile(fi); + System.gc(); + System.out.format("nextFile result: %d\n", result); + while (result == CDoc.OK) { + System.out.format("File %s length %d\n", fi.getName(), fi.getSize()); + byte[] dataout = new byte[(int) fi.getSize()]; + rdr.readData(dataout); + System.gc(); + System.out.format("Data OUT: %s\n", hex.formatHex(dataout)); + result = rdr.nextFile(fi); + } + rdr.finishDecryption(); + System.gc(); + } catch (CDocException exc) { + System.err.format("CDoc Exception %d: %s\n", exc.code, exc.getMessage()); + } + } + private static class ToolConf extends Configuration { public final HashMap values = new HashMap<>(); @@ -457,6 +538,15 @@ void setPassword(String password) { this.secret = password.getBytes(); } + @Override + public long random(DataBuffer dst, int size) throws CDocException { + SecureRandom random = new SecureRandom(); + byte bytes[] = new byte[size]; + random.nextBytes(bytes); + dst.setData(bytes); + return CDoc.OK; + } + @Override public long getSecret(DataBuffer dst, int idx) { dst.setData(secret); @@ -502,7 +592,7 @@ public long test(CertificateList dst) { private static class JavaLogger extends ILogger { @Override - public void LogMessage(ILogger.LogLevel level, String file, int line, String message) { + public void logMessage(LogLevel level, String file, int line, String message) { System.out.format("%s:%s %s %s\n", file, line, level, message); } } From 209042f0c756258aa08f6c47a2b858fc34c176e0 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 13 Feb 2026 14:24:19 +0200 Subject: [PATCH 11/16] Renamed ILogger -> Logger --- cdoc/CDoc.cpp | 3 +- cdoc/CDoc.h | 10 +- cdoc/CDoc1Reader.cpp | 1 - cdoc/CDoc1Writer.cpp | 1 - cdoc/CDoc2Reader.cpp | 1 - cdoc/CDoc2Writer.cpp | 1 - cdoc/CDocCipher.cpp | 1 - cdoc/CMakeLists.txt | 3 +- cdoc/Configuration.cpp | 1 - cdoc/ConsoleLogger.h | 47 ------- cdoc/Crypto.cpp | 1 - cdoc/CryptoBackend.cpp | 1 - cdoc/ILogger.h | 123 ------------------ cdoc/KeyShares.cpp | 1 - cdoc/Lock.cpp | 1 - cdoc/NetworkBackend.cpp | 1 - cdoc/PKCS11Backend.cpp | 1 - cdoc/Recipient.cpp | 1 - cdoc/Utils.cpp | 2 - cdoc/Utils.h | 42 ++++++ cdoc/cdoc-tool.cpp | 3 +- .../src/main/java/ee/ria/cdoc/CDocTool.java | 21 --- libcdoc.i | 7 +- 23 files changed, 50 insertions(+), 224 deletions(-) delete mode 100644 cdoc/ConsoleLogger.h delete mode 100644 cdoc/ILogger.h diff --git a/cdoc/CDoc.cpp b/cdoc/CDoc.cpp index 37cfb832..f98ef8bb 100644 --- a/cdoc/CDoc.cpp +++ b/cdoc/CDoc.cpp @@ -21,9 +21,10 @@ #include "CDoc2Writer.h" #include "CDoc2Reader.h" #include "Configuration.h" -#include "ConsoleLogger.h" #include "Io.h" #include "NetworkBackend.h" +#include "Utils.h" +#include "Logger.h" namespace libcdoc { diff --git a/cdoc/CDoc.h b/cdoc/CDoc.h index 4b048e36..7e1e2e8f 100644 --- a/cdoc/CDoc.h +++ b/cdoc/CDoc.h @@ -24,11 +24,6 @@ #include #include -#ifndef LIBCDOC_TESTING -// Remove this in production code -#define LIBCDOC_TESTING 1 -#endif - namespace libcdoc { /** @@ -136,9 +131,6 @@ CDOC_EXPORT std::string getVersion(); // Logging interface -class ILogger; -typedef ILogger Logger; - /** * @brief Log-level enumeration to indicate severity of the log message. */ @@ -175,6 +167,8 @@ enum LogLevel LEVEL_TRACE }; +class Logger; + CDOC_EXPORT void setLogger(Logger *logger); CDOC_EXPORT void log(LogLevel level, std::string_view file, int line, std::string_view msg); diff --git a/cdoc/CDoc1Reader.cpp b/cdoc/CDoc1Reader.cpp index ef3c3569..9d293b51 100644 --- a/cdoc/CDoc1Reader.cpp +++ b/cdoc/CDoc1Reader.cpp @@ -23,7 +23,6 @@ #include "Crypto.h" #include "CryptoBackend.h" #include "DDocReader.h" -#include "ILogger.h" #include "Lock.h" #include "Utils.h" #include "XmlReader.h" diff --git a/cdoc/CDoc1Writer.cpp b/cdoc/CDoc1Writer.cpp index 988a7a43..18847fc5 100644 --- a/cdoc/CDoc1Writer.cpp +++ b/cdoc/CDoc1Writer.cpp @@ -20,7 +20,6 @@ #include "Crypto.h" #include "DDocWriter.h" -#include "ILogger.h" #include "Recipient.h" #include "Utils.h" #include "XmlWriter.h" diff --git a/cdoc/CDoc2Reader.cpp b/cdoc/CDoc2Reader.cpp index d10e6ac3..23ab5781 100644 --- a/cdoc/CDoc2Reader.cpp +++ b/cdoc/CDoc2Reader.cpp @@ -22,7 +22,6 @@ #include "Configuration.h" #include "CryptoBackend.h" #include "CDoc2.h" -#include "ILogger.h" #include "KeyShares.h" #include "Lock.h" #include "NetworkBackend.h" diff --git a/cdoc/CDoc2Writer.cpp b/cdoc/CDoc2Writer.cpp index ba7f886e..a79271b6 100644 --- a/cdoc/CDoc2Writer.cpp +++ b/cdoc/CDoc2Writer.cpp @@ -21,7 +21,6 @@ #include "Configuration.h" #include "Crypto.h" #include "CDoc2.h" -#include "ILogger.h" #include "NetworkBackend.h" #include "Recipient.h" #include "Tar.h" diff --git a/cdoc/CDocCipher.cpp b/cdoc/CDocCipher.cpp index b1f3230d..c94efe9f 100644 --- a/cdoc/CDocCipher.cpp +++ b/cdoc/CDocCipher.cpp @@ -19,7 +19,6 @@ #include "CDocCipher.h" #include "CDocReader.h" #include "CDoc2.h" -#include "ILogger.h" #include "Lock.h" #include "NetworkBackend.h" #include "PKCS11Backend.h" diff --git a/cdoc/CMakeLists.txt b/cdoc/CMakeLists.txt index f55a826a..61e43697 100644 --- a/cdoc/CMakeLists.txt +++ b/cdoc/CMakeLists.txt @@ -15,8 +15,7 @@ set(PUBLIC_HEADERS CryptoBackend.h NetworkBackend.h PKCS11Backend.h - ILogger.h - ConsoleLogger.h + Logger.h ) add_library(cdoc_ver INTERFACE) diff --git a/cdoc/Configuration.cpp b/cdoc/Configuration.cpp index 0e4973c6..eee0586d 100644 --- a/cdoc/Configuration.cpp +++ b/cdoc/Configuration.cpp @@ -20,7 +20,6 @@ #include "Configuration.h" -#include "ILogger.h" #include "Utils.h" #include "json/picojson/picojson.h" diff --git a/cdoc/ConsoleLogger.h b/cdoc/ConsoleLogger.h deleted file mode 100644 index c1302b8f..00000000 --- a/cdoc/ConsoleLogger.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 . - * - */ - -#pragma once - -#include "ILogger.h" - -#include - -namespace libcdoc -{ - -/** - * @brief Console logger - * - * An ILogger subclass that logs text to console. - * - * Info messages are logged to cout, all others to cerr. - */ -class ConsoleLogger : public ILogger -{ -public: - virtual void logMessage(LogLevel level, std::string_view file, int line, std::string_view message) override - { - // We ignore by default the file name and line number, and call LogMessage with the level and message. - std::ostream& ofs = (level == LEVEL_INFO) ? std::cout : std::cerr; - ofs << message << '\n'; - } -}; - - -} diff --git a/cdoc/Crypto.cpp b/cdoc/Crypto.cpp index bf796ef1..d8adecd1 100644 --- a/cdoc/Crypto.cpp +++ b/cdoc/Crypto.cpp @@ -18,7 +18,6 @@ #include "CDoc.h" #include "Crypto.h" -#include "ILogger.h" #include "Utils.h" #define OPENSSL_SUPPRESS_DEPRECATED diff --git a/cdoc/CryptoBackend.cpp b/cdoc/CryptoBackend.cpp index 8c0f1652..56bc5ccd 100644 --- a/cdoc/CryptoBackend.cpp +++ b/cdoc/CryptoBackend.cpp @@ -18,7 +18,6 @@ #include "Crypto.h" #include "CryptoBackend.h" -#include "ILogger.h" #include "Utils.h" #define OPENSSL_SUPPRESS_DEPRECATED diff --git a/cdoc/ILogger.h b/cdoc/ILogger.h deleted file mode 100644 index e66acc81..00000000 --- a/cdoc/ILogger.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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 . - * - */ - -#ifndef __ILOGGER_H__INCLUDED__ -#define __ILOGGER_H__INCLUDED__ - -#include - -#include - -#ifdef __cpp_lib_format -#include -namespace fmt = std; -#else -#define FMT_HEADER_ONLY -#include "fmt/format.h" -#endif - -#include - -#define FORMAT fmt::format - -namespace libcdoc -{ - -/** - * @brief Generic interface to implement a logger. - */ -class CDOC_EXPORT ILogger -{ -public: - /** - * @brief Logs given message with given severity, file name and line number. - * - * It tests the log level and if <= min_level invokes logMessage - * - * @param level Severity of the log message. - * @param file File name where the log message was recorded. - * @param line Line number in the file where the log message was recorded. - * @param msg The log message. - */ - void log(LogLevel level, std::string_view file, int line, std::string_view msg) { - if (level <= min_level) logMessage(level, file, line, msg); - } - - /** - * @brief Sets minimum log level for the logger. - * @param level minimum level to log. - * - * Sets minimum level of log messages to log. For example, if the minimum log level is set - * to LogLevelInfo (default), then LogLevelFatal, LogLevelError, LogLevelWarning and LogLevelInfo - * messages are logged, but not LogLevelDebug or LogLevelTrace messages. - */ - void setMinLogLevel(LogLevel level) noexcept { min_level = level; } -protected: - /** - * @brief Logs given message with given severity, file name and line number. - * - * Every class implementing the ILogger interface must implement this member function. - * The efault implementation does nothing. - * The level should be checked by caller, thus the implementation should expect that level <= min_level - * - * @param level Severity of the log message. - * @param file File name where the log message was recorded. - * @param line Line number in the file where the log message was recorded. - * @param msg The log message. - */ - virtual void logMessage(LogLevel level, std::string_view file, int line, std::string_view msg) {} - - /** - * @brief Minimum level of log messages to log. - */ - LogLevel min_level = LEVEL_WARNING; -}; - -typedef ILogger Logger; - -#ifndef SWIG -template -static inline void LogFormat(LogLevel level, std::string_view file, int line, fmt::format_string fmt, Args&&... args) -{ - auto msg = fmt::format(fmt, std::forward(args)...); - libcdoc::log(level, file, line, msg); -} - -static inline void LogFormat(LogLevel level, std::string_view file, int line, std::string_view msg) -{ - libcdoc::log(level, file, line, msg); -} -#endif - -#define LOG(l,...) LogFormat((l), __FILE__, __LINE__, __VA_ARGS__) -#define LOG_ERROR(...) LogFormat(libcdoc::LEVEL_ERROR, __FILE__, __LINE__, __VA_ARGS__) -#define LOG_WARN(...) LogFormat(libcdoc::LEVEL_WARNING, __FILE__, __LINE__, __VA_ARGS__) -#define LOG_INFO(...) LogFormat(libcdoc::LEVEL_INFO, __FILE__, __LINE__, __VA_ARGS__) -#define LOG_DBG(...) LogFormat(libcdoc::LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__) - -#ifdef NDEBUG -#define LOG_TRACE(...) -#define LOG_TRACE_KEY(MSG, KEY) -#else -#define LOG_TRACE(...) LogFormat(libcdoc::LEVEL_TRACE, __FILE__, __LINE__, __VA_ARGS__) -#define LOG_TRACE_KEY(MSG, KEY) LogFormat(libcdoc::LEVEL_TRACE, __FILE__, __LINE__, MSG, toHex(KEY)) -#endif - -} - -#endif diff --git a/cdoc/KeyShares.cpp b/cdoc/KeyShares.cpp index d7996e6d..06d48875 100644 --- a/cdoc/KeyShares.cpp +++ b/cdoc/KeyShares.cpp @@ -23,7 +23,6 @@ #include "Crypto.h" #include "CryptoBackend.h" #include "NetworkBackend.h" -#include "ILogger.h" #include "Utils.h" #include "json/jwt.h" diff --git a/cdoc/Lock.cpp b/cdoc/Lock.cpp index 53d8affd..b5be546d 100644 --- a/cdoc/Lock.cpp +++ b/cdoc/Lock.cpp @@ -20,7 +20,6 @@ #include "Certificate.h" #include "Utils.h" -#include "ILogger.h" namespace libcdoc { diff --git a/cdoc/NetworkBackend.cpp b/cdoc/NetworkBackend.cpp index 3fb52d06..3382e3be 100644 --- a/cdoc/NetworkBackend.cpp +++ b/cdoc/NetworkBackend.cpp @@ -22,7 +22,6 @@ #include "CryptoBackend.h" #include "Utils.h" #include "utils/memory.h" -#include "ILogger.h" #define OPENSSL_SUPPRESS_DEPRECATED diff --git a/cdoc/PKCS11Backend.cpp b/cdoc/PKCS11Backend.cpp index 4d201769..914004c8 100644 --- a/cdoc/PKCS11Backend.cpp +++ b/cdoc/PKCS11Backend.cpp @@ -19,7 +19,6 @@ #include "PKCS11Backend.h" #include "Certificate.h" #include "Crypto.h" -#include "ILogger.h" #include "Utils.h" #include "pkcs11.h" diff --git a/cdoc/Recipient.cpp b/cdoc/Recipient.cpp index 16ad32c4..0e699844 100644 --- a/cdoc/Recipient.cpp +++ b/cdoc/Recipient.cpp @@ -21,7 +21,6 @@ #include "CDoc2.h" #include "Certificate.h" #include "Crypto.h" -#include "ILogger.h" #include "Utils.h" #include diff --git a/cdoc/Utils.cpp b/cdoc/Utils.cpp index 886035cb..27a83807 100644 --- a/cdoc/Utils.cpp +++ b/cdoc/Utils.cpp @@ -18,8 +18,6 @@ #include "Utils.h" -#include "ILogger.h" - #include "json/jwt.h" #include "json/picojson/picojson.h" diff --git a/cdoc/Utils.h b/cdoc/Utils.h index a98e4c83..ff0d4452 100644 --- a/cdoc/Utils.h +++ b/cdoc/Utils.h @@ -25,6 +25,20 @@ #include #include +#include + +#ifdef __cpp_lib_format +#include +namespace fmt = std; +#else +#define FMT_HEADER_ONLY +#include "fmt/format.h" +#endif + +#include + +#define FORMAT fmt::format + namespace libcdoc { std::string toBase64(const uint8_t *data, size_t len); @@ -137,6 +151,34 @@ std::vector toUint8Vector(const auto& data) std::string urlDecode(const std::string &src); +#ifndef SWIG +template +static inline void LogFormat(LogLevel level, std::string_view file, int line, fmt::format_string fmt, Args&&... args) +{ + auto msg = fmt::format(fmt, std::forward(args)...); + libcdoc::log(level, file, line, msg); +} + +static inline void LogFormat(LogLevel level, std::string_view file, int line, std::string_view msg) +{ + libcdoc::log(level, file, line, msg); +} +#endif + +#define LOG(l,...) LogFormat((l), __FILE__, __LINE__, __VA_ARGS__) +#define LOG_ERROR(...) LogFormat(libcdoc::LEVEL_ERROR, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_WARN(...) LogFormat(libcdoc::LEVEL_WARNING, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_INFO(...) LogFormat(libcdoc::LEVEL_INFO, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_DBG(...) LogFormat(libcdoc::LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__) + +#ifdef NDEBUG +#define LOG_TRACE(...) +#define LOG_TRACE_KEY(MSG, KEY) +#else +#define LOG_TRACE(...) LogFormat(libcdoc::LEVEL_TRACE, __FILE__, __LINE__, __VA_ARGS__) +#define LOG_TRACE_KEY(MSG, KEY) LogFormat(libcdoc::LEVEL_TRACE, __FILE__, __LINE__, MSG, toHex(KEY)) +#endif + } // namespace libcdoc #endif // UTILS_H diff --git a/cdoc/cdoc-tool.cpp b/cdoc/cdoc-tool.cpp index 9cd8403c..4ae763ca 100644 --- a/cdoc/cdoc-tool.cpp +++ b/cdoc/cdoc-tool.cpp @@ -23,9 +23,8 @@ #endif #include "CDocCipher.h" -#include "ConsoleLogger.h" -#include "ILogger.h" #include "Utils.h" +#include "Logger.h" #include "json/jwt.h" diff --git a/examples/java/src/main/java/ee/ria/cdoc/CDocTool.java b/examples/java/src/main/java/ee/ria/cdoc/CDocTool.java index 837d7ca9..00050c01 100644 --- a/examples/java/src/main/java/ee/ria/cdoc/CDocTool.java +++ b/examples/java/src/main/java/ee/ria/cdoc/CDocTool.java @@ -399,18 +399,6 @@ static void test() { CDocReader rdr = CDocReader.createReader(path, conf, null, network); System.err.format("Reader created (version %d)\n", rdr.getVersion()); - rdr.testConfig(buf); - System.err.format("Buffer out: %s\n", hex.formatHex(buf.getData())); - System.err.println("Success"); - - CertificateList certs = new CertificateList(); - rdr.testNetwork(certs); - System.err.format("Num certs: %s\n", certs.size()); - for (int i = 0; i < certs.size(); i++) { - byte[] cert = certs.getCertificate(i); - System.err.format(" %s\n", hex.formatHex(cert)); - } - testRW(conf, crypto); System.err.println("Success"); @@ -579,15 +567,6 @@ public long getPeerTLSCertificates(CertificateList dst, String url) throws CDocE System.err.println("ToolNetwork.getPeerTLSCertificates: " + dst); return CDoc.OK; } - - @Override - public long test(CertificateList dst) { - System.err.println("ToolNetwork.test: Java subclass implementation"); - System.err.format("dst: %s\n", dst); - dst.addCertificate(new byte[] {1, 2, 3}); - dst.addCertificate(new byte[] {4, 5, 6, 7, 8}); - return CDoc.OK; - } } private static class JavaLogger extends ILogger { diff --git a/libcdoc.i b/libcdoc.i index 7e800a76..83e1bef2 100644 --- a/libcdoc.i +++ b/libcdoc.i @@ -25,7 +25,7 @@ #include "Configuration.h" #include "CDocWriter.h" #include "CDocReader.h" -#include "ConsoleLogger.h" +#include "Logger.h" #include "Lock.h" #include "NetworkBackend.h" #include "PKCS11Backend.h" @@ -581,8 +581,6 @@ static std::vector SWIG_JavaArrayToVectorUnsignedChar(JNIEnv *jen // Swig does not like visibility/declspec attributes #define CDOC_EXPORT -// fixme: Remove this in production -#define LIBCDOC_TESTING 1 #define CDOC_DISABLE_MOVE(X) %include "CDoc.h" @@ -594,8 +592,7 @@ static std::vector SWIG_JavaArrayToVectorUnsignedChar(JNIEnv *jen %include "CryptoBackend.h" %include "NetworkBackend.h" %include "PKCS11Backend.h" -%include "ILogger.h" -%include "ConsoleLogger.h" +%include "Logger.h" // LockVector template must come after Lock.h is included so that // SWIG knows about the libcdoc::Lock class definition. From ee33b97b09c10d623b8de0030b99e6158ad27b16 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 13 Feb 2026 14:33:06 +0200 Subject: [PATCH 12/16] Added Logger.h --- cdoc/Logger.h | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 cdoc/Logger.h diff --git a/cdoc/Logger.h b/cdoc/Logger.h new file mode 100644 index 00000000..138ff044 --- /dev/null +++ b/cdoc/Logger.h @@ -0,0 +1,103 @@ +/* + * 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 . + * + */ + +#ifndef __LOGGER_H__INCLUDED__ +#define __LOGGER_H__INCLUDED__ + +#include +#include + +#include +#include + +namespace libcdoc +{ + +/** + * @brief Generic interface to implement a logger. + */ +class CDOC_EXPORT Logger +{ +public: + /** + * @brief Logs given message with given severity, file name and line number. + * + * It tests the log level and if <= min_level invokes logMessage + * + * @param level Severity of the log message. + * @param file File name where the log message was recorded. + * @param line Line number in the file where the log message was recorded. + * @param msg The log message. + */ + void log(LogLevel level, std::string_view file, int line, std::string_view msg) { + if (level <= min_level) logMessage(level, file, line, msg); + } + + /** + * @brief Sets minimum log level for the logger. + * @param level minimum level to log. + * + * Sets minimum level of log messages to log. For example, if the minimum log level is set + * to LEVEL_INFO (default), then LEVEL_FATAL, LEVEL_ERROR, LEVEL_WARNING and LEVEL_INFO + * messages are logged, but not LEVEL_DEBUG or LEVEL_TRACE messages. + */ + void setMinLogLevel(LogLevel level) noexcept { min_level = level; } + +protected: + /** + * @brief Logs given message with given severity, file name and line number. + * + * Every class implementing the ILogger interface must implement this member function. + * The efault implementation does nothing. + * The level should be checked by caller, thus the implementation should expect that level <= min_level + * + * @param level Severity of the log message. + * @param file File name where the log message was recorded. + * @param line Line number in the file where the log message was recorded. + * @param msg The log message. + */ + virtual void logMessage(LogLevel level, std::string_view file, int line, std::string_view msg) {} + + /** + * @brief Minimum level of log messages to log. + */ + LogLevel min_level = LEVEL_WARNING; +}; + +/** + * @brief Console logger + * + * An ILogger subclass that logs text to console. + * + * Info messages are logged to cout, all others to cerr. + */ + +class ConsoleLogger : public Logger +{ +public: + virtual void logMessage(LogLevel level, std::string_view file, int line, std::string_view message) override + { + // We ignore by default the file name and line number, and call LogMessage with the level and message. + std::ostream& ofs = (level == LEVEL_INFO) ? std::cout : std::cerr; + ofs << file << ':' << line << " " << message << '\n'; + } +}; + +} + +#endif From 9930c35a450579caf63a8796e0b2b40ceaa15002 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 13 Feb 2026 15:23:32 +0200 Subject: [PATCH 13/16] Removed ConsoleLogger from public interface --- cdoc/CDoc.cpp | 58 +++++++++++-------- cdoc/CDoc.h | 1 + cdoc/CDocReader.h | 4 -- cdoc/Configuration.h | 4 -- cdoc/Logger.h | 20 ------- cdoc/NetworkBackend.cpp | 9 --- cdoc/NetworkBackend.h | 4 -- cdoc/cdoc-tool.cpp | 57 ++++++++++++------ .../src/main/java/ee/ria/cdoc/CDocTool.java | 12 ++-- libcdoc.i | 2 +- 10 files changed, 77 insertions(+), 94 deletions(-) diff --git a/cdoc/CDoc.cpp b/cdoc/CDoc.cpp index f98ef8bb..546df084 100644 --- a/cdoc/CDoc.cpp +++ b/cdoc/CDoc.cpp @@ -26,6 +26,9 @@ #include "Utils.h" #include "Logger.h" +#include +#include + namespace libcdoc { struct Result { @@ -72,6 +75,29 @@ getVersion() return VERSION_STR; } +/** + * @brief Console logger + * + * An ILogger subclass that logs text to console. + * + * Info messages are logged to cout, all others to cerr. + */ + +class ConsoleLogger : public Logger +{ +public: + virtual void logMessage(LogLevel level, std::string_view file, int line, std::string_view message) override + { + // We ignore by default the file name and line number, and call LogMessage with the level and message. + std::ostream& ofs = (level == LEVEL_INFO) ? std::cout : std::cerr; + if (!file.empty()) { + ofs << std::filesystem::path(file).filename().string() << ':' << line << " " << message << '\n'; + } else { + ofs << message << '\n'; + } + } +}; + static Logger * getDefaultLogger() { @@ -87,6 +113,13 @@ setLogger(Logger *logger) sys_logger = logger; } +void +setLogLevel(LogLevel level) +{ + Logger *logger = (sys_logger) ? sys_logger : getDefaultLogger(); + logger->setMinLogLevel(level); +} + void log(LogLevel level, std::string_view file, int line, std::string_view msg) { @@ -162,31 +195,6 @@ libcdoc::CDocReader::createReader(std::istream& ifs, Configuration *conf, Crypto return createReader(isrc.release(), true, conf, crypto, network); } -#if LIBCDOC_TESTING -int64_t -libcdoc::CDocReader::testConfig(std::vector& dst) -{ - LOG_TRACE("CDocReader::testConfig::Native superclass"); - if (conf) { - LOG_DBG("CDocReader::testConfig this={} conf={}", reinterpret_cast(this), reinterpret_cast(conf)); - } - LOG_ERROR("CDocReader::testConfig::conf is null"); - return WORKFLOW_ERROR; -} - -int64_t -libcdoc::CDocReader::testNetwork(std::vector>& dst) -{ - LOG_TRACE("CDocReader::testNetwork::Native superclass"); - if (network) { - LOG_DBG("CDocReader::testNetwork this={} network={}", reinterpret_cast(this), reinterpret_cast(network)); - return network->test(dst); - } - LOG_ERROR("CDocReader::testNetwork::network is null"); - return WORKFLOW_ERROR; -} -#endif - libcdoc::CDocWriter::CDocWriter(int _version, DataConsumer *_dst, bool take_ownership) : version(_version), dst(_dst), owned(take_ownership) { diff --git a/cdoc/CDoc.h b/cdoc/CDoc.h index 7e1e2e8f..741b1e6c 100644 --- a/cdoc/CDoc.h +++ b/cdoc/CDoc.h @@ -170,6 +170,7 @@ enum LogLevel class Logger; CDOC_EXPORT void setLogger(Logger *logger); +CDOC_EXPORT void setLogLevel(LogLevel level); CDOC_EXPORT void log(LogLevel level, std::string_view file, int line, std::string_view msg); /** diff --git a/cdoc/CDocReader.h b/cdoc/CDocReader.h index 5c033aa1..c1fdf16b 100644 --- a/cdoc/CDocReader.h +++ b/cdoc/CDocReader.h @@ -200,10 +200,6 @@ class CDOC_EXPORT CDocReader { */ static CDocReader *createReader(std::istream& ifs, Configuration *conf, CryptoBackend *crypto, NetworkBackend *network); -#if LIBCDOC_TESTING - virtual int64_t testConfig(std::vector& dst); - virtual int64_t testNetwork(std::vector>& dst); -#endif protected: explicit CDocReader(int _version) : version(_version) {}; diff --git a/cdoc/Configuration.h b/cdoc/Configuration.h index 309c2e31..080e3fba 100644 --- a/cdoc/Configuration.h +++ b/cdoc/Configuration.h @@ -111,10 +111,6 @@ struct CDOC_EXPORT Configuration { * @return the key value */ int getInt(std::string_view param, int def_val = 0) const; - -#if LIBCDOC_TESTING - virtual int64_t test(std::vector& dst) { return OK; } -#endif }; /** diff --git a/cdoc/Logger.h b/cdoc/Logger.h index 138ff044..20f15ed3 100644 --- a/cdoc/Logger.h +++ b/cdoc/Logger.h @@ -22,7 +22,6 @@ #include #include -#include #include namespace libcdoc @@ -79,25 +78,6 @@ class CDOC_EXPORT Logger LogLevel min_level = LEVEL_WARNING; }; -/** - * @brief Console logger - * - * An ILogger subclass that logs text to console. - * - * Info messages are logged to cout, all others to cerr. - */ - -class ConsoleLogger : public Logger -{ -public: - virtual void logMessage(LogLevel level, std::string_view file, int line, std::string_view message) override - { - // We ignore by default the file name and line number, and call LogMessage with the level and message. - std::ostream& ofs = (level == LEVEL_INFO) ? std::cout : std::cerr; - ofs << file << ':' << line << " " << message << '\n'; - } -}; - } #endif diff --git a/cdoc/NetworkBackend.cpp b/cdoc/NetworkBackend.cpp index 3382e3be..eae5deab 100644 --- a/cdoc/NetworkBackend.cpp +++ b/cdoc/NetworkBackend.cpp @@ -155,15 +155,6 @@ libcdoc::NetworkBackend::getLastErrorStr(result_t code) const return libcdoc::getErrorStr(code); } -#if LIBCDOC_TESTING -int64_t -libcdoc::NetworkBackend::test(std::vector> &dst) -{ - LOG_TRACE("NetworkBackend::test::Native superclass"); - return OK; -} -#endif - // // Set peer certificate(s) for given server url // diff --git a/cdoc/NetworkBackend.h b/cdoc/NetworkBackend.h index 6c082240..446cff7d 100644 --- a/cdoc/NetworkBackend.h +++ b/cdoc/NetworkBackend.h @@ -277,10 +277,6 @@ struct CDOC_EXPORT NetworkBackend { result_t signMID(std::vector& dst, std::vector& cert, const std::string& url, const std::string& rp_uuid, const std::string& rp_name, const std::string& phone, const std::string& rcpt_id, const std::vector& digest, CryptoBackend::HashAlgorithm algo); - -#if LIBCDOC_TESTING - virtual int64_t test(std::vector> &dst); -#endif }; } // namespace libcdoc diff --git a/cdoc/cdoc-tool.cpp b/cdoc/cdoc-tool.cpp index 4ae763ca..30bd7364 100644 --- a/cdoc/cdoc-tool.cpp +++ b/cdoc/cdoc-tool.cpp @@ -31,6 +31,15 @@ using namespace std; using namespace libcdoc; +static std::map str2level = { + {"FATAL", libcdoc::LEVEL_FATAL}, + {"ERROR", libcdoc::LEVEL_ERROR}, + {"WARNING", libcdoc::LEVEL_WARNING}, + {"INFO", libcdoc::LEVEL_INFO}, + {"DEBUG", libcdoc::LEVEL_DEBUG}, + {"TRACE", libcdoc::LEVEL_TRACE} +}; + enum { RESULT_OK = 0, RESULT_ERROR, @@ -75,17 +84,8 @@ static void print_usage(ostream& ofs) ofs << " --library - path to the PKCS11 library to be used" << endl; ofs << " --server ID URL(S) - specifies a key or share server. The recipient key will be stored in server instead of in the document." << endl; ofs << " for key server the url is either fetch or send url. For share server it is comma-separated list of share server urls." << endl; - ofs << " --accept FILENAME - specifies an accepted server certificate (in der encoding)" << endl; - - //<< "cdoc-tool encrypt -r X509DerRecipientCert [-r X509DerRecipientCert [...]] InFile [InFile [...]] OutFile" << std::endl - // << "cdoc-tool encrypt --rcpt RECIPIENT [--rcpt RECIPIENT] [--file INFILE] [...] --out OUTFILE" << std::endl - // << " where RECIPIENT is in form label:TYPE:value" << std::endl - // << " where TYPE is 'cert', 'key' or 'pw'" << std::endl -#ifdef _WIN32 - // << "cdoc-tool decrypt win [ui|noui] pin InFile OutFolder" << endl -#endif - // << "cdoc-tool decrypt pkcs11 path/to/so pin InFile OutFolder" << std::endl - // << "cdoc-tool decrypt pkcs12 path/to/pkcs12 pin InFile OutFolder" << std::endl; + ofs << " --accept FILENAME - specifies an accepted server certificate (in der encoding)" << endl; + ofs << " --log-level LEVEL - set logging level (FATAL, ERROR, WARNING, INFO, DEBUG, TRACE)" << endl; } static std::vector @@ -134,6 +134,10 @@ parse_common(ToolConf& conf, int arg_idx, int argc, char *argv[]) } else if ((arg == "--conf") && ((arg_idx + 1) < argc)) { conf.parse(argv[arg_idx + 1]); return 2; + } else if ((arg == "--log-level") && ((arg_idx + 1) < argc)) { + if (!str2level.contains(argv[arg_idx + 1])) return 0; + libcdoc::setLogLevel(str2level[argv[arg_idx + 1]]); + return 2; } return 0; } @@ -635,11 +639,30 @@ static int ParseAndReEncrypt(int argc, char *argv[]) static int ParseAndGetLocks(int argc, char *argv[]) { - if (argc < 1) - return 2; + ToolConf conf; + std::string container; + + // + // Parse all arguments into ToolConf structure + // + int arg_idx = 0; + while (arg_idx < argc) { + int result = parse_common(conf, arg_idx, argc, argv); + if (result < 0) return result; + arg_idx += result; + if (result > 0) continue; + if (argv[arg_idx][0] == '-') { + return RESULT_USAGE; + } else { + if (!container.empty()) return RESULT_USAGE; + container = argv[arg_idx]; + } + arg_idx += 1; + } + if (container.empty()) return RESULT_USAGE; CDocCipher cipher; - cipher.Locks(argv[0]); + cipher.Locks(container.c_str()); return 0; } @@ -650,13 +673,9 @@ int main(int argc, char *argv[]) return 1; } - // Add console logger by default - ConsoleLogger console_logger; - console_logger.setMinLogLevel(LEVEL_TRACE); - libcdoc::setLogger(&console_logger); + libcdoc::setLogLevel(LEVEL_TRACE); string_view command(argv[1]); - LOG_INFO("Command: {}", command); CDocCipher cipher; int retVal = 2; // Output the help by default. diff --git a/examples/java/src/main/java/ee/ria/cdoc/CDocTool.java b/examples/java/src/main/java/ee/ria/cdoc/CDocTool.java index 00050c01..13e932ee 100644 --- a/examples/java/src/main/java/ee/ria/cdoc/CDocTool.java +++ b/examples/java/src/main/java/ee/ria/cdoc/CDocTool.java @@ -33,8 +33,8 @@ public static String getArg(int arg_idx, String[] args) { return args[arg_idx]; } - // Make logger static to ensure that it is not garbage-collected as long as it is atached to library - private static ILogger logger; + // Make logger static to ensure that it is not garbage-collected as long as it is attached to library + private static Logger logger; public static void main(String[] args) { System.out.println("Starting app..."); @@ -137,8 +137,7 @@ public static void main(String[] args) { System.load(lib.getAbsolutePath()); System.out.println("Library loaded"); - //logger = new JavaLogger(); - logger = new ConsoleLogger(); + logger = new JavaLogger(); logger.setMinLogLevel(log_level); CDoc.setLogger(logger); CDoc.log(LogLevel.LEVEL_DEBUG, "FILENAME", 0, "Starting CDocTool.java"); @@ -215,9 +214,6 @@ static void decrypt(String file, String label, String password) { CDocReader rdr = CDocReader.createReader(src, false, conf, crypto, null); System.out.format("Reader created (version %d)\n", rdr.getVersion()); - //rdr.testConfig(buf); - System.err.format("Buffer out: %s\n", hex.formatHex(buf.getData())); - LockVector locks = rdr.getLocks(); for (int idx = 0; idx < locks.size(); idx++) { ee.ria.cdoc.Lock lock = locks.get(idx); @@ -569,7 +565,7 @@ public long getPeerTLSCertificates(CertificateList dst, String url) throws CDocE } } - private static class JavaLogger extends ILogger { + private static class JavaLogger extends Logger { @Override public void logMessage(LogLevel level, String file, int line, String message) { System.out.format("%s:%s %s %s\n", file, line, level, message); diff --git a/libcdoc.i b/libcdoc.i index 83e1bef2..7f32dc2d 100644 --- a/libcdoc.i +++ b/libcdoc.i @@ -218,7 +218,7 @@ %feature("director") libcdoc::PKCS11Backend; %feature("director") libcdoc::NetworkBackend; %feature("director") libcdoc::Configuration; -%feature("director") libcdoc::ILogger; +%feature("director") libcdoc::Logger; #ifdef SWIGJAVA %include "arrays_java.i" From ce13f363185c116f6a92a1812152339bade0d4bd Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 13 Feb 2026 15:45:10 +0200 Subject: [PATCH 14/16] Fixed WinBackend.cpp --- cdoc/WinBackend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdoc/WinBackend.cpp b/cdoc/WinBackend.cpp index 5abb5731..de61a20c 100644 --- a/cdoc/WinBackend.cpp +++ b/cdoc/WinBackend.cpp @@ -19,7 +19,7 @@ #include "WinBackend.h" #include "CDoc2.h" -#include "ILogger.h" +#include "Logger.h" #include #include From 4453784acb4a148fe5c6d2ac400c65f7f1595ecf Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 13 Feb 2026 16:06:37 +0200 Subject: [PATCH 15/16] Fixed WinBackend.cpp more --- cdoc/WinBackend.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/cdoc/WinBackend.cpp b/cdoc/WinBackend.cpp index de61a20c..4c725459 100644 --- a/cdoc/WinBackend.cpp +++ b/cdoc/WinBackend.cpp @@ -18,6 +18,7 @@ #include "WinBackend.h" +#include "CDoc.h" #include "CDoc2.h" #include "Logger.h" From cb101793cba0fb777bbdfb849c82e2a00f04bd9a Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 13 Feb 2026 16:23:19 +0200 Subject: [PATCH 16/16] One more Winbackend fix --- cdoc/WinBackend.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdoc/WinBackend.cpp b/cdoc/WinBackend.cpp index 4c725459..fbc8b55a 100644 --- a/cdoc/WinBackend.cpp +++ b/cdoc/WinBackend.cpp @@ -18,9 +18,9 @@ #include "WinBackend.h" -#include "CDoc.h" #include "CDoc2.h" #include "Logger.h" +#include "Utils.h" #include #include