From 25ab00796435f45c492d7883f0ccf56f2fea7879 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 23 Jan 2026 16:42:05 +0200 Subject: [PATCH 01/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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/31] 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 From 7d5f35dc77cbdb5c08737b7f7026e967605868f4 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Tue, 3 Mar 2026 13:59:43 +0200 Subject: [PATCH 17/31] Export CDocCipher internally for cdoc-tool --- cdoc/CDocCipher.h | 3 ++- cdoc/CMakeLists.txt | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cdoc/CDocCipher.h b/cdoc/CDocCipher.h index 42b76cfb..3138dc50 100644 --- a/cdoc/CDocCipher.h +++ b/cdoc/CDocCipher.h @@ -23,6 +23,7 @@ #include "CDocWriter.h" #include "RcptInfo.h" #include "ToolConf.h" +#include "Exports.h" #include #include @@ -31,7 +32,7 @@ namespace libcdoc { -class CDocCipher +class CDOC_EXPORT CDocCipher { public: CDocCipher() = default; diff --git a/cdoc/CMakeLists.txt b/cdoc/CMakeLists.txt index 61e43697..9117edaa 100644 --- a/cdoc/CMakeLists.txt +++ b/cdoc/CMakeLists.txt @@ -58,6 +58,7 @@ add_library(cdoc CDoc2.h Wrapper.h utils/memory.h + CDocCipher.cpp CDocCipher.h ) set_target_properties(cdoc PROPERTIES VERSION ${PROJECT_VERSION} @@ -89,9 +90,9 @@ target_link_libraries(cdoc PRIVATE ) if(BUILD_TOOLS) - add_executable(cdoc-tool cdoc-tool.cpp CDocCipher.cpp) + add_executable(cdoc-tool cdoc-tool.cpp) target_include_directories(cdoc-tool PRIVATE ${OPENSSL_INCLUDE_DIR}) - target_link_libraries(cdoc-tool cdoc_ver cdoc OpenSSL::SSL) + target_link_libraries(cdoc-tool cdoc_ver cdoc) set_target_properties(cdoc-tool PROPERTIES INSTALL_RPATH $<$:@executable_path/../lib> ) From 1022623d13bdec661a341ecdb22cbb367f951097 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Wed, 11 Mar 2026 18:40:33 +0200 Subject: [PATCH 18/31] Fix label parsing --- cdoc/Recipient.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/cdoc/Recipient.cpp b/cdoc/Recipient.cpp index 0e699844..d5acb415 100644 --- a/cdoc/Recipient.cpp +++ b/cdoc/Recipient.cpp @@ -257,7 +257,11 @@ map Recipient::parseLabel(const string& label) string::size_type base64IndPos = label_wo_prefix.find(LABELBASE64IND); if (base64IndPos == string::npos) { - label_to_prcss = std::move(label_wo_prefix); + if (label_wo_prefix.starts_with(",")) { + label_to_prcss = label_wo_prefix.substr(1); + } else { + label_to_prcss = std::move(label_wo_prefix); + } } else { @@ -278,7 +282,10 @@ map Recipient::parseLabel(const string& label) } else { - parsed_label[urlDecode(label_data_parts[0])] = urlDecode(label_data_parts[1]); + std::string key = urlDecode(label_data_parts[0]); + std::string value = urlDecode(label_data_parts[1]); + std::transform(key.begin(), key.end(), key.begin(), [](unsigned char c){ return std::tolower(c); }); + parsed_label[key] = value; } } From fe80e763b3ac091fb91a14164c661fc5a0cb72ff Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 13 Mar 2026 17:20:11 +0200 Subject: [PATCH 19/31] Handle "PaxHeaders" paths correctly --- cdoc/Tar.cpp | 141 ++++++++++++++++++++++++++++++++++----------------- cdoc/Tar.h | 4 +- 2 files changed, 98 insertions(+), 47 deletions(-) diff --git a/cdoc/Tar.cpp b/cdoc/Tar.cpp index c4775fdb..32a11f1b 100644 --- a/cdoc/Tar.cpp +++ b/cdoc/Tar.cpp @@ -96,7 +96,15 @@ struct libcdoc::Header { referenceChecksum == checkSum.second; } - bool operator==(const Header&) const = default; + std::string getName() const { + return std::string(name.data(), std::min(name.size(), strlen(name.data()))); + } + + int64_t getSize() const { + return fromOctal(size); + } + + bool operator==(const Header&) const = default; }; static_assert (sizeof(Header) == BLOCKSIZE, "Header struct size is incorrect"); @@ -145,8 +153,10 @@ libcdoc::TarConsumer::writeHeader(const Header &h) noexcept { } libcdoc::result_t -libcdoc::TarConsumer::writeHeader(Header &h, int64_t size) noexcept { +libcdoc::TarConsumer::writeHeader(Header &h, const std::string& name, int64_t size) noexcept { h.chksum.fill(' '); + size_t len = std::min(name.size(), h.name.size()); + std::copy_n(name.cbegin(), len, h.name.begin()); toOctal(h.size, size); toOctal(h.chksum, h.checksum().first); return writeHeader(h); @@ -205,18 +215,30 @@ libcdoc::TarConsumer::open(const std::string& name, int64_t size) _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()); - // TODO: Create pax record if name contains special symbols - if(name.size() > 100 || size > 07777777) { + bool need_pax_name = (name.size() >= 100); + for (auto c : name) { + if ((c & 0x80) || (c < ' ')) { + need_pax_name = true; + break; + } + } + if(need_pax_name || size > 07777777) { + LOG_DBG("Writing Pax header: name {} size {}", name, size); h.typeflag = 'x'; std::string paxData; - if(name.size() > 100) + if(need_pax_name) paxData += toPaxRecord("path", name); if(size > 07777777) paxData += toPaxRecord("size", std::to_string(size)); - if (auto rv = writeHeader(h, paxData.size()); rv != OK) + std::filesystem::path path(name); + if (path.has_parent_path()) { + path = path.parent_path() / "PaxHeaders.X" / path.filename(); + } else { + path = std::filesystem::path("./PaxHeaders.X") / path.filename(); + } + LOG_DBG("Pax path: {}", path.string()); + if (auto rv = writeHeader(h, path.string(), paxData.size()); rv != OK) return rv; if (auto rv = _dst->write((const uint8_t *) paxData.data(), paxData.size()); rv != paxData.size()) return rv < OK ? rv : OUTPUT_ERROR; @@ -224,7 +246,7 @@ libcdoc::TarConsumer::open(const std::string& name, int64_t size) return rv; } h.typeflag = '0'; - return writeHeader(h, size); + return writeHeader(h, name, size); } libcdoc::TarSource::TarSource(DataSource *src, bool take_ownership) @@ -271,11 +293,54 @@ libcdoc::TarSource::isEof() noexcept return _eof; } +libcdoc::result_t +libcdoc::TarSource::readPaxHeader(const Header& hdr, std::string& name, int64_t& size) +{ + int64_t h_size = hdr.getSize(); + std::vector pax_in(h_size); + result_t result = _src->read((uint8_t *) pax_in.data(), pax_in.size()); + if (result != h_size) { + _error = INPUT_STREAM_ERROR; + return _error; + } + std::string paxData(pax_in.data(), pax_in.size()); + _src->skip(padding(h_size)); + // Parse Pax data + std::stringstream ss(paxData); + for(const std::string &data: split(paxData, '\n')) { + if(data.empty()) break; + size_t eq_pos = data.find_first_of('='); + if (eq_pos == std::string::npos) { + _error = DATA_FORMAT_ERROR; + return _error; + } + std::string headerValue = data.substr(eq_pos + 1, data.size() - eq_pos - 1); + size_t sp_pos = data.find_first_of(' '); + if ((sp_pos == std::string::npos) || (sp_pos >= eq_pos)) { + _error = DATA_FORMAT_ERROR; + return _error; + } + std::string lenStr = data.substr(0, sp_pos); + std::string keyWord = data.substr(sp_pos + 1, eq_pos - sp_pos - 1); + if(data.size() + 1 != stoi(lenStr)) { + _error = DATA_FORMAT_ERROR; + return _error; + } + LOG_DBG("PAX {} : {}", keyWord, headerValue); + if(keyWord == "path") + name = std::move(headerValue); + if(keyWord == "size") + size = stoll(headerValue); + } + return OK; +} + libcdoc::result_t libcdoc::TarSource::next(std::string& name, int64_t& size) { Header h; + // Skip if not at the start of a block if (_pos < _block_size) { int64_t result = _src->skip(_block_size - _pos); _pos = 0; @@ -286,18 +351,23 @@ libcdoc::TarSource::next(std::string& name, int64_t& size) return _error; } } + while (!_src->isEof()) { + // Read header int64_t result = _src->read((uint8_t *)&h, BLOCKSIZE); if (result != BLOCKSIZE) { _error = INPUT_STREAM_ERROR; return _error; } if (h.isNull()) { + // Two null headers mark end of archive + LOG_DBG("NULL header"); result = _src->read((uint8_t *)&h, BLOCKSIZE); if (result != BLOCKSIZE) { _error = INPUT_STREAM_ERROR; return _error; } + LOG_DBG("EOF"); _eof = true; return END_OF_STREAM; } @@ -305,18 +375,15 @@ libcdoc::TarSource::next(std::string& name, int64_t& size) _error = DATA_FORMAT_ERROR; return _error; } + LOG_DBG("Header typeflag {} name {} size {}", h.typeflag, h.getName(), h.getSize()); - std::string h_name = std::string(h.name.data(), std::min(h.name.size(), strlen(h.name.data()))); - size_t h_size = fromOctal(h.size); + std::string h_name; + int64_t h_size = -1; if(h.typeflag == 'x') { - std::vector pax_in(h_size); - result = _src->read((uint8_t *) pax_in.data(), pax_in.size()); - if (result != h_size) { - _error = INPUT_STREAM_ERROR; + _error = readPaxHeader(h, h_name, h_size); + if (_error != OK) return _error; - } - std::string paxData(pax_in.data(), pax_in.size()); - _src->skip(padding(h_size)); + // Read ustar header result = _src->read((uint8_t *)&h, BLOCKSIZE); if (result != BLOCKSIZE) { _error = INPUT_STREAM_ERROR; @@ -326,40 +393,22 @@ libcdoc::TarSource::next(std::string& name, int64_t& size) _error = DATA_FORMAT_ERROR; return _error; } - h_size = fromOctal(h.size); - std::stringstream ss(paxData); - for(const std::string &data: split(paxData, '\n')) { - if(data.empty()) break; - size_t eq_pos = data.find_first_of('='); - if (eq_pos == std::string::npos) { - _error = DATA_FORMAT_ERROR; - return _error; - } - std::string headerValue = data.substr(eq_pos + 1, data.size() - eq_pos - 1); - size_t sp_pos = data.find_first_of(' '); - if ((sp_pos == std::string::npos) || (sp_pos >= eq_pos)) { - _error = DATA_FORMAT_ERROR; - return _error; - } - std::string lenStr = data.substr(0, sp_pos); - std::string keyWord = data.substr(sp_pos + 1, eq_pos - sp_pos - 1); - if(data.size() + 1 != stoi(lenStr)) { - _error = DATA_FORMAT_ERROR; - return _error; - } - if(keyWord == "path") h_name = std::move(headerValue); - if(keyWord == "size") h_size = stoi(headerValue); + if(h.typeflag != '0' && h.typeflag != 0) { + _error = DATA_FORMAT_ERROR; + return _error; } } - if(h.typeflag == '0' || h.typeflag == 0) { - name = std::move(h_name); - size = h_size; + if (h.typeflag == '0' || h.typeflag == 0) { + name = (h_name.empty()) ? h.getName() : std::move(h_name); + size = (h_size < 0) ? h.getSize() : h_size; _pos = 0; - _data_size = h_size; - _block_size = h_size + padding(h_size); + _data_size = size; + _block_size = size + padding(size); _eof = false; return OK; } else { + // Skip other header types ('g') + h_size = h.getSize(); _src->skip(h_size + padding(h_size)); } } diff --git a/cdoc/Tar.h b/cdoc/Tar.h index 0ec7c832..a75be590 100644 --- a/cdoc/Tar.h +++ b/cdoc/Tar.h @@ -37,7 +37,7 @@ struct TarConsumer final : public MultiDataConsumer libcdoc::result_t open(const std::string& name, int64_t size) final; private: result_t writeHeader(const Header &h) noexcept; - result_t writeHeader(Header &h, int64_t size) noexcept; + result_t writeHeader(Header &h, const std::string& name, int64_t size) noexcept; result_t writePadding(int64_t size) noexcept; DataConsumer *_dst; @@ -64,6 +64,8 @@ struct TarSource : public MultiDataSource size_t _block_size; size_t _data_size; size_t _pos; + + libcdoc::result_t readPaxHeader(const Header& hdr, std::string& name, int64_t& size); }; } // namespace libcdoc From 1be01a709ef9aaba7c322b6fed3caf6fa2f9e4cb Mon Sep 17 00:00:00 2001 From: lauris71 Date: Mon, 16 Mar 2026 17:31:22 +0200 Subject: [PATCH 20/31] Apply suggestions from code review Co-authored-by: Raul Metsma --- cdoc/Tar.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cdoc/Tar.cpp b/cdoc/Tar.cpp index 32a11f1b..90484854 100644 --- a/cdoc/Tar.cpp +++ b/cdoc/Tar.cpp @@ -297,16 +297,14 @@ libcdoc::result_t libcdoc::TarSource::readPaxHeader(const Header& hdr, std::string& name, int64_t& size) { int64_t h_size = hdr.getSize(); - std::vector pax_in(h_size); - result_t result = _src->read((uint8_t *) pax_in.data(), pax_in.size()); + std::string paxData(h_size, 0); + result_t result = _src->read((uint8_t *) paxData.data(), paxData.size()); if (result != h_size) { _error = INPUT_STREAM_ERROR; return _error; } - std::string paxData(pax_in.data(), pax_in.size()); _src->skip(padding(h_size)); // Parse Pax data - std::stringstream ss(paxData); for(const std::string &data: split(paxData, '\n')) { if(data.empty()) break; size_t eq_pos = data.find_first_of('='); From 7d533a0d0d00d68c360c63f978b8eebf82fe4f42 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Wed, 25 Mar 2026 15:29:28 +0200 Subject: [PATCH 21/31] Fix tar filenames if only PAX size is used --- cdoc/Tar.cpp | 15 +++++++-------- cdoc/Tar.h | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/cdoc/Tar.cpp b/cdoc/Tar.cpp index 90484854..02077958 100644 --- a/cdoc/Tar.cpp +++ b/cdoc/Tar.cpp @@ -153,7 +153,9 @@ libcdoc::TarConsumer::writeHeader(const Header &h) noexcept { } libcdoc::result_t -libcdoc::TarConsumer::writeHeader(Header &h, const std::string& name, int64_t size) noexcept { +libcdoc::TarConsumer::writeHeader(const std::string& name, int64_t size, char typeflag) noexcept { + Header h {}; + h.typeflag = typeflag; h.chksum.fill(' '); size_t len = std::min(name.size(), h.name.size()); std::copy_n(name.cbegin(), len, h.name.begin()); @@ -214,7 +216,6 @@ libcdoc::TarConsumer::open(const std::string& name, int64_t size) _current_size = size; _current_written = 0; - Header h {}; bool need_pax_name = (name.size() >= 100); for (auto c : name) { @@ -225,7 +226,6 @@ libcdoc::TarConsumer::open(const std::string& name, int64_t size) } if(need_pax_name || size > 07777777) { LOG_DBG("Writing Pax header: name {} size {}", name, size); - h.typeflag = 'x'; std::string paxData; if(need_pax_name) paxData += toPaxRecord("path", name); @@ -238,15 +238,14 @@ libcdoc::TarConsumer::open(const std::string& name, int64_t size) path = std::filesystem::path("./PaxHeaders.X") / path.filename(); } LOG_DBG("Pax path: {}", path.string()); - if (auto rv = writeHeader(h, path.string(), paxData.size()); rv != OK) + if (auto rv = writeHeader(path.string(), paxData.size(), 'x'); rv != OK) return rv; if (auto rv = _dst->write((const uint8_t *) paxData.data(), paxData.size()); rv != paxData.size()) return rv < OK ? rv : OUTPUT_ERROR; if (auto rv = writePadding(paxData.size()); rv != OK) return rv; } - h.typeflag = '0'; - return writeHeader(h, name, size); + return writeHeader(name, size, '0'); } libcdoc::TarSource::TarSource(DataSource *src, bool take_ownership) @@ -297,8 +296,8 @@ libcdoc::result_t libcdoc::TarSource::readPaxHeader(const Header& hdr, std::string& name, int64_t& size) { int64_t h_size = hdr.getSize(); - std::string paxData(h_size, 0); - result_t result = _src->read((uint8_t *) paxData.data(), paxData.size()); + std::string paxData(h_size, 0); + result_t result = _src->read((uint8_t *) paxData.data(), paxData.size()); if (result != h_size) { _error = INPUT_STREAM_ERROR; return _error; diff --git a/cdoc/Tar.h b/cdoc/Tar.h index a75be590..729b83b9 100644 --- a/cdoc/Tar.h +++ b/cdoc/Tar.h @@ -37,7 +37,7 @@ struct TarConsumer final : public MultiDataConsumer libcdoc::result_t open(const std::string& name, int64_t size) final; private: result_t writeHeader(const Header &h) noexcept; - result_t writeHeader(Header &h, const std::string& name, int64_t size) noexcept; + result_t writeHeader(const std::string& name, int64_t size, char typeflag = '0') noexcept; result_t writePadding(int64_t size) noexcept; DataConsumer *_dst; From 8bb1c866b8ea93d930c65f5620fc9cdd2f03ea75 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Thu, 26 Mar 2026 13:03:29 +0200 Subject: [PATCH 22/31] Moved parseLabel to Lock, take minimum of expiry time --- cdoc/CDoc2.h | 19 ++++++++++ cdoc/CDoc2Writer.cpp | 2 ++ cdoc/CDocCipher.cpp | 2 +- cdoc/Lock.cpp | 56 ++++++++++++++++++++++++++++++ cdoc/Lock.h | 7 ++++ cdoc/Recipient.cpp | 78 ++---------------------------------------- cdoc/Recipient.h | 7 ---- test/libcdoc_boost.cpp | 7 ++-- 8 files changed, 91 insertions(+), 87 deletions(-) diff --git a/cdoc/CDoc2.h b/cdoc/CDoc2.h index 61c38451..ce3813cc 100644 --- a/cdoc/CDoc2.h +++ b/cdoc/CDoc2.h @@ -43,6 +43,25 @@ std::string getSaltForExpand(const std::string& label); // Get salt bitstring for HKDF expand method std::string getSaltForExpand(const std::vector& key_material, const std::vector& rcpt_key); +/** + * @brief Prefix with what starts machine generated Lock's label. + */ +constexpr std::string_view LABELPREFIX{"data:"}; + +/** + * @brief String after label prefix indicating, the rest of the label is Base64 encoded. + */ +constexpr std::string_view LABELBASE64IND{";base64,"}; + +/** + * @brief EID type values for machine-readable label + */ +static constexpr std::string_view eid_strs[] = { + "Unknown", + "ID-card", + "Digi-ID", + "Digi-ID E-RESIDENT" +}; } // namespace CDoc2 } // namespace libcdoc diff --git a/cdoc/CDoc2Writer.cpp b/cdoc/CDoc2Writer.cpp index a79271b6..eceeeffe 100644 --- a/cdoc/CDoc2Writer.cpp +++ b/cdoc/CDoc2Writer.cpp @@ -119,6 +119,7 @@ createRSAServerCapsule(flatbuffers::FlatBufferBuilder& builder, const libcdoc::R rsaKeyServer.Union(), builder.CreateString(rcpt.server_id), builder.CreateString(transaction_id)); + if (rcpt.expiry_ts) expiry_time = std::min(rcpt.expiry_ts, expiry_time); return cdoc20::header::CreateRecipientRecord(builder, cdoc20::header::Capsule::recipients_KeyServerCapsule, capsule.Union(), @@ -153,6 +154,7 @@ createECCServerCapsule(flatbuffers::FlatBufferBuilder& builder, const libcdoc::R eccKeyServer.Union(), builder.CreateString(rcpt.server_id), builder.CreateString(transaction_id)); + if (rcpt.expiry_ts) expiry_time = std::min(rcpt.expiry_ts, expiry_time); return cdoc20::header::CreateRecipientRecord(builder, cdoc20::header::Capsule::recipients_KeyServerCapsule, capsule.Union(), diff --git a/cdoc/CDocCipher.cpp b/cdoc/CDocCipher.cpp index c94efe9f..b8575ddf 100644 --- a/cdoc/CDocCipher.cpp +++ b/cdoc/CDocCipher.cpp @@ -673,7 +673,7 @@ void CDocCipher::Locks(const char* file) const int lock_id = 1; for (const Lock& lock : rdr->getLocks()) { - map parsed_label(Recipient::parseLabel(lock.label)); + map parsed_label(Lock::parseLabel(lock.label)); if (parsed_label.empty()) { // Human-readable label cout << lock_id << ": " << lock.label << endl; diff --git a/cdoc/Lock.cpp b/cdoc/Lock.cpp index b5be546d..6d715ba9 100644 --- a/cdoc/Lock.cpp +++ b/cdoc/Lock.cpp @@ -18,6 +18,7 @@ #include "Lock.h" +#include "CDoc2.h" #include "Certificate.h" #include "Utils.h" @@ -52,5 +53,60 @@ Lock::setInt(Params key, int32_t val) params[key] = std::move(bytes); } +std::map +Lock::parseLabel(const std::string& label) +{ + std::map parsed_label; + // Check if provided label starts with the machine generated label prefix. + if (!label.starts_with(CDoc2::LABELPREFIX)) + { + return parsed_label; + } + + std::string label_wo_prefix(label.substr(CDoc2::LABELPREFIX.size())); + + // Label to be processed + std::string label_to_prcss; + + // We ignore mediatype part + + // Check, if the label is Base64 encoded + auto base64IndPos = label_wo_prefix.find(CDoc2::LABELBASE64IND); + if (base64IndPos == std::string::npos) + { + if (label_wo_prefix.starts_with(",")) { + label_to_prcss = label_wo_prefix.substr(1); + } else { + label_to_prcss = std::move(label_wo_prefix); + } + } + else + { + std::string base64_label(label_wo_prefix.substr(base64IndPos + CDoc2::LABELBASE64IND.size())); + std::vector decodedLabel(fromBase64(base64_label)); + label_to_prcss.assign(decodedLabel.cbegin(), decodedLabel.cend()); + } + + auto label_parts(split(label_to_prcss, '&')); + for (auto& part : label_parts) + { + auto label_data_parts(split(part, '=')); + if (label_data_parts.size() != 2) + { + // Invalid label data. We just ignore them. + LOG_ERROR("The label '{}' is invalid", label); + } + else + { + std::string key = urlDecode(label_data_parts[0]); + std::string value = urlDecode(label_data_parts[1]); + std::transform(key.begin(), key.end(), key.begin(), [](unsigned char c){ return std::tolower(c); }); + parsed_label[key] = value; + } + } + + return parsed_label; +} + } // namespace libcdoc diff --git a/cdoc/Lock.h b/cdoc/Lock.h index 250eeb85..07225e8d 100644 --- a/cdoc/Lock.h +++ b/cdoc/Lock.h @@ -239,6 +239,13 @@ struct CDOC_EXPORT Lock */ void setInt(Params param, int32_t val); + /** + * @brief parse machine-readable CDoc2 label + * @param label the label + * @return a map of key-value pairs + */ + static std::map parseLabel(const std::string& label); + bool operator== (const Lock& other) const noexcept = default; private: diff --git a/cdoc/Recipient.cpp b/cdoc/Recipient.cpp index 02928900..3459f41d 100644 --- a/cdoc/Recipient.cpp +++ b/cdoc/Recipient.cpp @@ -30,26 +30,6 @@ using namespace std; namespace libcdoc { -/** - * @brief Prefix with what starts machine generated Lock's label. - */ -constexpr string_view LABELPREFIX{"data:"}; - -/** - * @brief String after label prefix indicating, the rest of the label is Base64 encoded. - */ -constexpr string_view LABELBASE64IND{";base64,"}; - -/** - * @brief EID type values for machine-readable label - */ -static constexpr std::string_view eid_strs[] = { - "Unknown", - "ID-card", - "Digi-ID", - "Digi-ID E-RESIDENT" -}; - Recipient Recipient::makeSymmetric(std::string label, int32_t kdf_iter) { @@ -143,7 +123,7 @@ Recipient::isTheSameRecipient(const std::vector& public_key) const static void buildLabel(std::ostream& ofs, std::string_view type, std::initializer_list> components) { - ofs << LABELPREFIX; + ofs << CDoc2::LABELPREFIX; ofs << "v" << '=' << std::to_string(CDoc2::KEYLABELVERSION) << '&' << "type" << '=' << type; for (const auto& [key, value] : components) { @@ -155,7 +135,7 @@ buildLabel(std::ostream& ofs, std::string_view type, std::initializer_list Recipient::parseLabel(const string& label) -{ - map parsed_label; - // Check if provided label starts with the machine generated label prefix. - if (!label.starts_with(LABELPREFIX)) - { - return parsed_label; - } - - string label_wo_prefix(label.substr(LABELPREFIX.size())); - - // Label to be processed - string label_to_prcss; - - // We ignore mediatype part - - // Check, if the label is Base64 encoded - string::size_type base64IndPos = label_wo_prefix.find(LABELBASE64IND); - if (base64IndPos == string::npos) - { - if (label_wo_prefix.starts_with(",")) { - label_to_prcss = label_wo_prefix.substr(1); - } else { - label_to_prcss = std::move(label_wo_prefix); - } - } - else - { - string base64_label(label_wo_prefix.substr(base64IndPos + LABELBASE64IND.size())); - vector decodedLabel(fromBase64(base64_label)); - label_to_prcss.assign(decodedLabel.cbegin(), decodedLabel.cend()); - } - - vector label_parts(split(label_to_prcss, '&')); - for (vector::const_reference part : label_parts) - { - vector label_data_parts(split(part, '=')); - if (label_data_parts.size() != 2) - { - // Invalid label data. We just ignore them. - LOG_ERROR("The label '{}' is invalid", label); - } - else - { - std::string key = urlDecode(label_data_parts[0]); - std::string value = urlDecode(label_data_parts[1]); - std::transform(key.begin(), key.end(), key.begin(), [](unsigned char c){ return std::tolower(c); }); - parsed_label[key] = value; - } - } - - return parsed_label; -} - } // namespace libcdoc diff --git a/cdoc/Recipient.h b/cdoc/Recipient.h index 0b04755b..f1aa2698 100644 --- a/cdoc/Recipient.h +++ b/cdoc/Recipient.h @@ -235,13 +235,6 @@ struct CDOC_EXPORT Recipient { */ std::string getLabel(const std::vector> &extra) const; - /** - * @brief parse machine-readable CDoc2 label - * @param label the label - * @return a map of key-value pairs - */ - static std::map parseLabel(const std::string& label); - bool operator== (const Recipient& other) const = default; protected: Recipient(Type _type) : type(_type) {}; diff --git a/test/libcdoc_boost.cpp b/test/libcdoc_boost.cpp index 1401dc39..88a221d3 100644 --- a/test/libcdoc_boost.cpp +++ b/test/libcdoc_boost.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -731,7 +732,7 @@ BOOST_AUTO_TEST_CASE(PlainLabelParsing) { const string label("data:v=1&type=ID-card&serial_number=PNOEE-38001085718&cn=J%C3%95EORG%2CJAAK-KRISTJAN%2C38001085718"); - auto result = libcdoc::Recipient::parseLabel(label); + auto result = libcdoc::Lock::parseLabel(label); for (const auto& [key, value] : ExpectedParsedLabel) { auto result_pair = result.find(key); @@ -747,7 +748,7 @@ BOOST_AUTO_TEST_CASE(Base64LabelParsing) { const string label("data:;base64,dj0xJnR5cGU9SUQtY2FyZCZzZXJpYWxfbnVtYmVyPVBOT0VFLTM4MDAxMDg1NzE4JmNuPUolQzMlOTVFT1JHJTJDSkFBSy1LUklTVEpBTiUyQzM4MDAxMDg1NzE4"); - auto result = libcdoc::Recipient::parseLabel(label); + auto result = libcdoc::Lock::parseLabel(label); for (const auto& [key, value] : ExpectedParsedLabel) { auto result_pair = result.find(key); @@ -763,7 +764,7 @@ BOOST_AUTO_TEST_CASE(Base64LabelParsingWithMediaType) { const string label("data:application/x-www-form-urlencoded;base64,dj0xJnR5cGU9SUQtY2FyZCZzZXJpYWxfbnVtYmVyPVBOT0VFLTM4MDAxMDg1NzE4JmNuPUolQzMlOTVFT1JHJTJDSkFBSy1LUklTVEpBTiUyQzM4MDAxMDg1NzE4"); - auto result = libcdoc::Recipient::parseLabel(label); + auto result = libcdoc::Lock::parseLabel(label); for (const auto& [key, value] : ExpectedParsedLabel) { auto result_pair = result.find(key); From a9bb1376f888c0277805aea1b9a8d4b0604b7bf7 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Thu, 26 Mar 2026 13:18:29 +0200 Subject: [PATCH 23/31] Fixed CDocTool.java --- examples/java/src/main/java/ee/ria/cdoc/CDocTool.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 13e932ee..e2a2f7c5 100644 --- a/examples/java/src/main/java/ee/ria/cdoc/CDocTool.java +++ b/examples/java/src/main/java/ee/ria/cdoc/CDocTool.java @@ -369,7 +369,7 @@ static void locks(String path) { static void test() { System.err.println("Testing label generation"); 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); + java.util.Map map = ee.ria.cdoc.Lock.parseLabel(label); for (String key : map.keySet()) { System.err.format(" %s:%s\n", key, map.get(key)); } From 84a4a92fbf2e80a9e22009a8024da78623d6c36a Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 27 Mar 2026 10:11:49 +0200 Subject: [PATCH 24/31] Use property system for automatic label generation --- cdoc/CDoc.h | 37 ++++++++++++++++++ cdoc/CDoc2.h | 22 ++++++----- cdoc/CDoc2Reader.cpp | 44 +++++++++++----------- cdoc/CDoc2Writer.cpp | 38 +++++++++---------- cdoc/Lock.cpp | 8 ++-- cdoc/Recipient.cpp | 89 +++++++++++++++++++------------------------- cdoc/Recipient.h | 26 ++++++++----- cdoc/WinBackend.cpp | 2 +- 8 files changed, 150 insertions(+), 116 deletions(-) diff --git a/cdoc/CDoc.h b/cdoc/CDoc.h index 960f4727..a6500e2b 100644 --- a/cdoc/CDoc.h +++ b/cdoc/CDoc.h @@ -183,6 +183,43 @@ struct FileInfo { int64_t size; }; +/** + * @brief CDoc2-specific public values + * + * This is class (instead of namespace) to streamline Swig wrapping + */ +struct CDOC_EXPORT CDoc2 final { + + /** + * @brief Recipient types for machine-readable labels + * + */ + static constexpr std::string_view TYPE_PASSWORD = "pw"; + static constexpr std::string_view TYPE_SYMMETRIC = "secret"; + static constexpr std::string_view TYPE_PUBLIC_KEY = "pub_key"; + static constexpr std::string_view TYPE_CERTIFICATE = "cert"; + static constexpr std::string_view TYPE_UNKNOWN = "Unknown"; + static constexpr std::string_view TYPE_ID_CARD = "ID-card"; + static constexpr std::string_view TYPE_DIGI_ID = "Digi-ID"; + static constexpr std::string_view TYPE_DIGI_ID_E_RESIDENT = "Digi-ID E-RESIDENT"; + + /** + * @brief Recipient data for machine-readable labels + * + */ + static constexpr std::string_view LBL_VERSION = "v"; + static constexpr std::string_view LBL_TYPE = "type"; + static constexpr std::string_view LBL_FILE = "file"; + static constexpr std::string_view LBL_LABEL = "label"; + static constexpr std::string_view LBL_CN = "cn"; + static constexpr std::string_view LBL_SERIAL_NUMBER = "serial_number"; + static constexpr std::string_view LBL_LAST_NAME = "last_name"; + static constexpr std::string_view LBL_FIRST_NAME = "first_name"; + static constexpr std::string_view LBL_CERT_SHA1 = "cert_sha1"; + + CDoc2() = delete; +}; + }; // namespace libcdoc #endif // CDOC_H diff --git a/cdoc/CDoc2.h b/cdoc/CDoc2.h index ce3813cc..9f7b3902 100644 --- a/cdoc/CDoc2.h +++ b/cdoc/CDoc2.h @@ -19,10 +19,12 @@ #ifndef __CDOC2_H__ #define __CDOC2_H__ +#include "CDoc.h" + #include namespace libcdoc { -namespace CDoc2 { +namespace CDoc2Internal { static constexpr std::string_view LABEL = "CDOC\x02"; static constexpr std::string_view CEK = "CDOC20cek"; @@ -53,15 +55,15 @@ constexpr std::string_view LABELPREFIX{"data:"}; */ constexpr std::string_view LABELBASE64IND{";base64,"}; -/** - * @brief EID type values for machine-readable label - */ -static constexpr std::string_view eid_strs[] = { - "Unknown", - "ID-card", - "Digi-ID", - "Digi-ID E-RESIDENT" -}; + /** + * @brief EID type values for machine-readable label + */ + static constexpr std::string_view eid_strs[] = { + CDoc2::TYPE_UNKNOWN, + CDoc2::TYPE_ID_CARD, + CDoc2::TYPE_DIGI_ID, + CDoc2::TYPE_DIGI_ID_E_RESIDENT + }; } // namespace CDoc2 } // namespace libcdoc diff --git a/cdoc/CDoc2Reader.cpp b/cdoc/CDoc2Reader.cpp index 478e51ab..daeb688d 100644 --- a/cdoc/CDoc2Reader.cpp +++ b/cdoc/CDoc2Reader.cpp @@ -47,19 +47,19 @@ using namespace libcdoc; // Get salt bitstring for HKDF expand method std::string -libcdoc::CDoc2::getSaltForExpand(const std::string& label) +libcdoc::CDoc2Internal::getSaltForExpand(const std::string& label) { std::ostringstream oss; - oss << libcdoc::CDoc2::KEK << cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) << label; + oss << libcdoc::CDoc2Internal::KEK << cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) << label; return oss.str(); } // Get salt bitstring for HKDF expand method std::string -libcdoc::CDoc2::getSaltForExpand(const std::vector& key_material, const std::vector& rcpt_key) +libcdoc::CDoc2Internal::getSaltForExpand(const std::vector& key_material, const std::vector& rcpt_key) { std::ostringstream oss; - oss << libcdoc::CDoc2::KEK + oss << libcdoc::CDoc2Internal::KEK << cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) << std::string_view((const char*)rcpt_key.data(), rcpt_key.size()) << std::string_view((const char*)key_material.data(), key_material.size()); @@ -149,7 +149,7 @@ CDoc2Reader::getFMK(std::vector& fmk, unsigned int lock_idx) if (lock.type == Lock::Type::PASSWORD) { // Password LOG_DBG("password"); - std::string info_str = libcdoc::CDoc2::getSaltForExpand(lock.label); + std::string info_str = libcdoc::CDoc2Internal::getSaltForExpand(lock.label); LOG_DBG("info: {}", toHex(info_str)); std::vector kek_pm; 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) { @@ -163,7 +163,7 @@ CDoc2Reader::getFMK(std::vector& fmk, unsigned int lock_idx) } else if (lock.type == Lock::Type::SYMMETRIC_KEY) { // Symmetric key LOG_DBG("symmetric"); - std::string info_str = libcdoc::CDoc2::getSaltForExpand(lock.label); + std::string info_str = libcdoc::CDoc2Internal::getSaltForExpand(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) { @@ -217,16 +217,16 @@ CDoc2Reader::getFMK(std::vector& fmk, unsigned int lock_idx) } } else { std::vector kek_pm; - int result = crypto->deriveHMACExtract(kek_pm, key_material, toUint8Vector(libcdoc::CDoc2::KEKPREMASTER), lock_idx); + int result = crypto->deriveHMACExtract(kek_pm, key_material, toUint8Vector(libcdoc::CDoc2Internal::KEKPREMASTER), lock_idx); if (result < 0) { setLastError(crypto->getLastErrorStr(result)); 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)); + std::string info_str = libcdoc::CDoc2Internal::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); + kek = libcdoc::Crypto::expand(kek_pm, info_str, libcdoc::CDoc2Internal::KEY_LEN); } } else if (lock.type == Lock::Type::SHARE_SERVER) { /* SALT */ @@ -345,7 +345,7 @@ CDoc2Reader::getFMK(std::vector& fmk, unsigned int lock_idx) LOG_ERROR("{}", last_error); return libcdoc::CRYPTO_ERROR; } - std::vector hhk = libcdoc::Crypto::expand(fmk, libcdoc::CDoc2::HMAC); + std::vector hhk = libcdoc::Crypto::expand(fmk, libcdoc::CDoc2Internal::HMAC); LOG_TRACE_KEY("xor: {}", lock.encrypted_fmk); LOG_TRACE_KEY("fmk: {}", fmk); @@ -409,11 +409,11 @@ CDoc2Reader::beginDecryption(const std::vector& fmk) } } priv->_at_nonce = false; - std::vector cek = libcdoc::Crypto::expand(fmk, libcdoc::CDoc2::CEK); + std::vector cek = libcdoc::Crypto::expand(fmk, libcdoc::CDoc2Internal::CEK); LOG_TRACE_KEY("cek: {}", cek); - priv->dec = std::make_unique(*priv->_src, EVP_chacha20_poly1305(), cek, libcdoc::CDoc2::NONCE_LEN); - std::vector aad = toUint8Vector(libcdoc::CDoc2::PAYLOAD); + priv->dec = std::make_unique(*priv->_src, EVP_chacha20_poly1305(), cek, libcdoc::CDoc2Internal::NONCE_LEN); + std::vector aad = toUint8Vector(libcdoc::CDoc2Internal::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) { @@ -621,16 +621,16 @@ CDoc2Reader::CDoc2Reader(libcdoc::DataSource *src, bool take_ownership) setLastError(t_("Invalid CDoc 2.0 header")); - uint8_t in[libcdoc::CDoc2::LABEL.size()]; - if (priv->_src->read(in, libcdoc::CDoc2::LABEL.size()) != libcdoc::CDoc2::LABEL.size()) { + uint8_t in[libcdoc::CDoc2Internal::LABEL.size()]; + if (priv->_src->read(in, libcdoc::CDoc2Internal::LABEL.size()) != libcdoc::CDoc2Internal::LABEL.size()) { LOG_ERROR("{}", last_error); return; } - if (memcmp(libcdoc::CDoc2::LABEL.data(), in, libcdoc::CDoc2::LABEL.size())) { + if (memcmp(libcdoc::CDoc2Internal::LABEL.data(), in, libcdoc::CDoc2Internal::LABEL.size())) { LOG_ERROR("{}", last_error); return; } - //if (libcdoc::CDoc2::LABEL.compare(0, libcdoc::CDoc2::LABEL.size(), (const char *) in)) return; + //if (libcdoc::CDoc2Internal::LABEL.compare(0, libcdoc::CDoc2Internal::LABEL.size(), (const char *) in)) return; // Read 32-bit header length in big endian order uint8_t c[4]; @@ -648,13 +648,13 @@ CDoc2Reader::CDoc2Reader(libcdoc::DataSource *src, bool take_ownership) LOG_ERROR("{}", last_error); return; } - priv->headerHMAC.resize(libcdoc::CDoc2::KEY_LEN); - if (priv->_src->read(priv->headerHMAC.data(), libcdoc::CDoc2::KEY_LEN) != libcdoc::CDoc2::KEY_LEN) { + priv->headerHMAC.resize(libcdoc::CDoc2Internal::KEY_LEN); + if (priv->_src->read(priv->headerHMAC.data(), libcdoc::CDoc2Internal::KEY_LEN) != libcdoc::CDoc2Internal::KEY_LEN) { LOG_ERROR("{}", last_error); return; } - priv->_nonce_pos = libcdoc::CDoc2::LABEL.size() + 4 + header_len + libcdoc::CDoc2::KEY_LEN; + priv->_nonce_pos = libcdoc::CDoc2Internal::LABEL.size() + 4 + header_len + libcdoc::CDoc2Internal::KEY_LEN; priv->_at_nonce = true; flatbuffers::Verifier verifier(priv->header_data.data(), priv->header_data.size()); @@ -687,12 +687,12 @@ CDoc2Reader::CDoc2Reader(libcdoc::DataSource *src, bool take_ownership) bool CDoc2Reader::isCDoc2File(libcdoc::DataSource *src) { - std::array in {}; + std::array in {}; if (src->read(in.data(), in.size()) != in.size()) { LOG_DBG("CDoc2Reader::isCDoc2File: Cannot read tag"); return false; } - if (libcdoc::CDoc2::LABEL.compare(0, in.size(), (char *) in.data(), in.size())) { + if (libcdoc::CDoc2Internal::LABEL.compare(0, in.size(), (char *) in.data(), in.size())) { LOG_DBG("CDoc2Reader::isCDoc2File: Invalid tag: {}", toHex(in)); return false; } diff --git a/cdoc/CDoc2Writer.cpp b/cdoc/CDoc2Writer.cpp index eceeeffe..349aa0f6 100644 --- a/cdoc/CDoc2Writer.cpp +++ b/cdoc/CDoc2Writer.cpp @@ -49,9 +49,9 @@ CDoc2Writer::writeHeader(const std::vector &recipients) return libcdoc::WRONG_ARGUMENTS; } std::vector rnd; - if(auto rv = crypto->random(rnd, libcdoc::CDoc2::KEY_LEN); rv < 0) + if(auto rv = crypto->random(rnd, libcdoc::CDoc2Internal::KEY_LEN); rv < 0) return rv; - std::vector fmk = libcdoc::Crypto::extract(rnd, {libcdoc::CDoc2::SALT.cbegin(), libcdoc::CDoc2::SALT.cend()}); + std::vector fmk = libcdoc::Crypto::extract(rnd, {libcdoc::CDoc2Internal::SALT.cbegin(), libcdoc::CDoc2Internal::SALT.cend()}); std::fill(rnd.begin(), rnd.end(), 0); LOG_TRACE_KEY("fmk: {}", fmk); @@ -62,8 +62,8 @@ CDoc2Writer::writeHeader(const std::vector &recipients) return rv; } - auto hhk = libcdoc::Crypto::expand(fmk, libcdoc::CDoc2::HMAC); - auto cek = libcdoc::Crypto::expand(fmk, libcdoc::CDoc2::CEK); + auto hhk = libcdoc::Crypto::expand(fmk, libcdoc::CDoc2Internal::HMAC); + auto cek = libcdoc::Crypto::expand(fmk, libcdoc::CDoc2Internal::CEK); std::fill(fmk.begin(), fmk.end(), 0); LOG_TRACE_KEY("cek: {}", cek); LOG_TRACE_KEY("hhk: {}", hhk); @@ -75,16 +75,16 @@ CDoc2Writer::writeHeader(const std::vector &recipients) // FIXME: not big/little endian friendly uint8_t header_len[] {uint8_t(hs >> 24), uint8_t((hs >> 16) & 0xff), uint8_t((hs >> 8) & 0xff), uint8_t(hs & 0xff)}; - dst->write((const uint8_t *) libcdoc::CDoc2::LABEL.data(), libcdoc::CDoc2::LABEL.size()); + dst->write((const uint8_t *) libcdoc::CDoc2Internal::LABEL.data(), libcdoc::CDoc2Internal::LABEL.size()); dst->write((const uint8_t *) &header_len, 4); dst->write(header.data(), header.size()); dst->write(headerHMAC.data(), headerHMAC.size()); std::vector nonce; - crypto->random(nonce, libcdoc::CDoc2::NONCE_LEN); + crypto->random(nonce, libcdoc::CDoc2Internal::NONCE_LEN); LOG_TRACE_KEY("nonce: {}", nonce); auto cipher = std::make_unique(*dst, EVP_chacha20_poly1305(), Crypto::Key(std::move(cek), nonce)); - std::vector aad(libcdoc::CDoc2::PAYLOAD.cbegin(), libcdoc::CDoc2::PAYLOAD.cend()); + std::vector aad(libcdoc::CDoc2Internal::PAYLOAD.cbegin(), libcdoc::CDoc2Internal::PAYLOAD.cend()); aad.insert(aad.end(), header.cbegin(), header.cend()); aad.insert(aad.end(), headerHMAC.cbegin(), headerHMAC.cend()); if(auto rv = cipher->writeAAD(aad); rv < 0) @@ -198,7 +198,7 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vector> fb_rcpts; - std::vector xor_key(libcdoc::CDoc2::KEY_LEN); + std::vector xor_key(libcdoc::CDoc2Internal::KEY_LEN); for (unsigned int rcpt_idx = 0; rcpt_idx < recipients.size(); rcpt_idx++) { const libcdoc::Recipient& rcpt = recipients.at(rcpt_idx); if (rcpt.isPKI()) { @@ -223,7 +223,7 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vectorrandom(kek, libcdoc::CDoc2::KEY_LEN); + crypto->random(kek, libcdoc::CDoc2Internal::KEY_LEN); if (libcdoc::Crypto::xor_data(xor_key, fmk, kek) != libcdoc::OK) { setLastError("Internal error"); LOG_ERROR("{}", last_error); @@ -267,8 +267,8 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vector sharedSecret = libcdoc::Crypto::deriveSharedSecret(ephKey.get(), publicKey.get()); key_material = libcdoc::Crypto::toPublicKeyDer(ephKey.get()); - std::vector kekPm = libcdoc::Crypto::extract(sharedSecret, std::vector(libcdoc::CDoc2::KEKPREMASTER.cbegin(), libcdoc::CDoc2::KEKPREMASTER.cend())); - std::string info_str = libcdoc::CDoc2::getSaltForExpand(key_material, rcpt.rcpt_key); + std::vector kekPm = libcdoc::Crypto::extract(sharedSecret, std::vector(libcdoc::CDoc2Internal::KEKPREMASTER.cbegin(), libcdoc::CDoc2Internal::KEKPREMASTER.cend())); + std::string info_str = libcdoc::CDoc2Internal::getSaltForExpand(key_material, rcpt.rcpt_key); kek = libcdoc::Crypto::expand(kekPm, info_str, fmk.size()); if (libcdoc::Crypto::xor_data(xor_key, fmk, kek) != libcdoc::OK) { @@ -302,16 +302,16 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vector kek_pm(libcdoc::CDoc2::KEY_LEN); + std::string info_str = libcdoc::CDoc2Internal::getSaltForExpand(rcpt.getLabel({})); + std::vector kek_pm(libcdoc::CDoc2Internal::KEY_LEN); std::vector salt; - int64_t result = crypto->random(salt, libcdoc::CDoc2::KEY_LEN); + int64_t result = crypto->random(salt, libcdoc::CDoc2Internal::KEY_LEN); if (result < 0) { setLastError(crypto->getLastErrorStr(result)); return result; } std::vector pw_salt; - result = crypto->random(pw_salt, libcdoc::CDoc2::KEY_LEN); + result = crypto->random(pw_salt, libcdoc::CDoc2Internal::KEY_LEN); if (result < 0) { setLastError(crypto->getLastErrorStr(result)); return result; @@ -321,7 +321,7 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vectorgetLastErrorStr(result)); return result; } - std::vector kek = libcdoc::Crypto::expand(kek_pm, info_str, libcdoc::CDoc2::KEY_LEN); + std::vector kek = libcdoc::Crypto::expand(kek_pm, info_str, libcdoc::CDoc2Internal::KEY_LEN); LOG_DBG("Label: {}", rcpt.label); LOG_DBG("KDF iter: {}", rcpt.kdf_iter); @@ -368,11 +368,11 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vector key_material_salt; - crypto->random(key_material_salt, libcdoc::CDoc2::KEY_LEN); + crypto->random(key_material_salt, libcdoc::CDoc2Internal::KEY_LEN); //KeyMaterial_i = CSRNG(256) std::vector key_material; - crypto->random(key_material, libcdoc::CDoc2::KEY_LEN); + crypto->random(key_material, libcdoc::CDoc2Internal::KEY_LEN); //KEK_i_pm = HKDF_Extract(KeyMaterialSalt_i, KeyMaterial_i) std::vector kek_pm = libcdoc::Crypto::extract(key_material_salt, key_material); @@ -394,7 +394,7 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vector> kek_shares(N_SHARES); for (int i = 1; i < N_SHARES; i++) { // KEK_i_share_j = CSRNG(256) - crypto->random(kek_shares[i], libcdoc::CDoc2::KEY_LEN); + crypto->random(kek_shares[i], libcdoc::CDoc2Internal::KEY_LEN); } // KEK_i_share_1 = XOR(KEK_i, KEK_i_share_2, KEK_i_share_3,..., KEK_i_share_n) kek_shares[0] = std::move(kek); diff --git a/cdoc/Lock.cpp b/cdoc/Lock.cpp index 6d715ba9..83bcda24 100644 --- a/cdoc/Lock.cpp +++ b/cdoc/Lock.cpp @@ -58,12 +58,12 @@ Lock::parseLabel(const std::string& label) { std::map parsed_label; // Check if provided label starts with the machine generated label prefix. - if (!label.starts_with(CDoc2::LABELPREFIX)) + if (!label.starts_with(CDoc2Internal::LABELPREFIX)) { return parsed_label; } - std::string label_wo_prefix(label.substr(CDoc2::LABELPREFIX.size())); + std::string label_wo_prefix(label.substr(CDoc2Internal::LABELPREFIX.size())); // Label to be processed std::string label_to_prcss; @@ -71,7 +71,7 @@ Lock::parseLabel(const std::string& label) // We ignore mediatype part // Check, if the label is Base64 encoded - auto base64IndPos = label_wo_prefix.find(CDoc2::LABELBASE64IND); + auto base64IndPos = label_wo_prefix.find(CDoc2Internal::LABELBASE64IND); if (base64IndPos == std::string::npos) { if (label_wo_prefix.starts_with(",")) { @@ -82,7 +82,7 @@ Lock::parseLabel(const std::string& label) } else { - std::string base64_label(label_wo_prefix.substr(base64IndPos + CDoc2::LABELBASE64IND.size())); + std::string base64_label(label_wo_prefix.substr(base64IndPos + CDoc2Internal::LABELBASE64IND.size())); std::vector decodedLabel(fromBase64(base64_label)); label_to_prcss.assign(decodedLabel.cbegin(), decodedLabel.cend()); } diff --git a/cdoc/Recipient.cpp b/cdoc/Recipient.cpp index 3459f41d..91fd3a43 100644 --- a/cdoc/Recipient.cpp +++ b/cdoc/Recipient.cpp @@ -121,60 +121,44 @@ Recipient::isTheSameRecipient(const std::vector& public_key) const } static void -buildLabel(std::ostream& ofs, std::string_view type, std::initializer_list> components) +buildLabel(std::ostream& ofs, std::string_view type, const std::map lbl_parts, std::initializer_list> extra) { - ofs << CDoc2::LABELPREFIX; - ofs << "v" << '=' << std::to_string(CDoc2::KEYLABELVERSION) << '&' - << "type" << '=' << type; - for (const auto& [key, value] : components) { + auto parts = lbl_parts; + if (parts.contains("v")) + parts.erase("v"); + if (parts.contains("type")) + parts.erase("type"); + for (const auto& [key, value] : extra) { + if (!value.empty()) + parts[key] = value; + } + ofs << CDoc2Internal::LABELPREFIX; + ofs << CDoc2::LBL_VERSION << '=' << std::to_string(CDoc2Internal::KEYLABELVERSION) << '&' + << CDoc2::LBL_TYPE << '=' << type; + for (const auto& [key, value] : parts) { if (!value.empty()) ofs << '&' << urlEncode(key) << '=' << urlEncode(value); } } static void -BuildLabelEID(std::ostream& ofs, Certificate::EIDType type, const Certificate& x509) -{ - buildLabel(ofs, CDoc2::eid_strs[type], { - {"cn", x509.getCommonName()}, - {"serial_number", x509.getSerialNumber()}, - {"last_name", x509.getSurname()}, - {"first_name", x509.getGivenName()}, - }); -} - -static void -BuildLabelCertificate(std::ostream &ofs, const std::string& file, const Certificate& x509) +BuildLabelEID(std::ostream& ofs, Certificate::EIDType type, const Certificate& x509, const std::map& lbl_parts) { - buildLabel(ofs, "cert", { - {"file", file}, - {"cn", x509.getCommonName()}, - {"cert_sha1", toHex(x509.getDigest())} + + buildLabel(ofs, CDoc2Internal::eid_strs[type], lbl_parts, { + {CDoc2::LBL_CN, x509.getCommonName()}, + {CDoc2::LBL_SERIAL_NUMBER, x509.getSerialNumber()}, + {CDoc2::LBL_LAST_NAME, x509.getSurname()}, + {CDoc2::LBL_FIRST_NAME, x509.getGivenName()}, }); } static void -BuildLabelPublicKey(std::ostream &ofs, const std::string& file) +BuildLabelCertificate(std::ostream &ofs, const Certificate& x509, const std::map& lbl_parts) { - buildLabel(ofs, "pub_key", { - {"file", file} - }); -} - -static void -BuildLabelSymmetricKey(std::ostream &ofs, const std::string& label, const std::string& file) -{ - buildLabel(ofs, "secret", { - {"label", label}, - {"file", file} - }); -} - -static void -BuildLabelPassword(std::ostream &ofs, const std::string& label) -{ - buildLabel(ofs, "pw", { - {"label", label} + buildLabel(ofs, CDoc2::TYPE_CERTIFICATE, lbl_parts, { + {CDoc2::LBL_CN, x509.getCommonName()}, + {CDoc2::LBL_CERT_SHA1, toHex(x509.getDigest())} }); } @@ -183,6 +167,15 @@ Recipient::getLabel(const std::vector parts; + for (const auto& [key, value] : lbl_parts) { + if (!value.empty()) + parts[key] = value; + } + for (const auto& [key, value] : extra) { + if (!value.empty()) + parts[key] = value; + } std::ostringstream ofs; switch(type) { case NONE: @@ -190,30 +183,26 @@ Recipient::getLabel(const std::vector 0) { - BuildLabelPassword(ofs, key_name); + buildLabel(ofs, CDoc2::TYPE_PASSWORD, parts, {}); } else { - BuildLabelSymmetricKey(ofs, key_name, file_name); + buildLabel(ofs, CDoc2::TYPE_SYMMETRIC, parts, {}); } break; case PUBLIC_KEY: if (!cert.empty()) { Certificate x509(cert); if (auto eid = x509.getEIDType(); eid != Certificate::Unknown) { - BuildLabelEID(ofs, eid, x509); + BuildLabelEID(ofs, eid, x509, parts); } else { - BuildLabelCertificate(ofs, file_name, x509); + BuildLabelCertificate(ofs, x509, parts); } } else { - BuildLabelPublicKey(ofs, file_name); + buildLabel(ofs, CDoc2::TYPE_PUBLIC_KEY, parts, {}); } break; case KEYSHARE: break; } - for (const auto& [key, value] : extra) { - if (!value.empty()) - ofs << '&' << urlEncode(key) << '=' << urlEncode(value); - } LOG_DBG("Generated label: {}", ofs.str()); return ofs.str(); } diff --git a/cdoc/Recipient.h b/cdoc/Recipient.h index f1aa2698..d51da86a 100644 --- a/cdoc/Recipient.h +++ b/cdoc/Recipient.h @@ -109,16 +109,6 @@ struct CDOC_EXPORT Recipient { * */ uint64_t expiry_ts = 0; - /** - * @brief key/certificate filename for machine-readable label - * - */ - std::string file_name; - /** - * @brief public key/password name for machine-readable label - * - */ - std::string key_name; /** * @brief test whether the Recipient structure is initialized @@ -235,9 +225,25 @@ struct CDOC_EXPORT Recipient { */ std::string getLabel(const std::vector> &extra) const; + /** + * @brief Set a property for automatic label generation + * + * @param key the property name + * @param value the property value + */ + void setLabelValue(std::string_view key, std::string_view value) { + if (!value.empty()) { + lbl_parts[std::string(key)] = value; + } else { + lbl_parts.erase(std::string(key)); + } + } + bool operator== (const Recipient& other) const = default; protected: Recipient(Type _type) : type(_type) {}; +private: + std::map lbl_parts; }; } // namespace libcdoc diff --git a/cdoc/WinBackend.cpp b/cdoc/WinBackend.cpp index fbc8b55a..588d01a4 100644 --- a/cdoc/WinBackend.cpp +++ b/cdoc/WinBackend.cpp @@ -244,7 +244,7 @@ libcdoc::WinBackend::deriveHMACExtract(std::vector& dst, const std::vec dst.resize(int(size)); err = NCryptDeriveKey(sharedSecret, BCRYPT_KDF_HMAC, ¶ms, PBYTE(dst.data()), size, &size, 0); if (err == ERROR_SUCCESS) { - dst.resize(CDoc2::KEY_LEN); + dst.resize(CDoc2Internal::KEY_LEN); result = OK; } } From 4fb60a3756c0a28ca0cd199e5465d791900c1f1a Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 27 Mar 2026 11:10:35 +0200 Subject: [PATCH 25/31] remove final from CDoc2 --- cdoc/CDoc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdoc/CDoc.h b/cdoc/CDoc.h index a6500e2b..2497eabd 100644 --- a/cdoc/CDoc.h +++ b/cdoc/CDoc.h @@ -188,7 +188,7 @@ struct FileInfo { * * This is class (instead of namespace) to streamline Swig wrapping */ -struct CDOC_EXPORT CDoc2 final { +struct CDOC_EXPORT CDoc2 { /** * @brief Recipient types for machine-readable labels From 7f94de49bf92fc77b4f24ae1637a16ef41128b6c Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 27 Mar 2026 12:01:16 +0200 Subject: [PATCH 26/31] Exclude CDoc2 from swig for the time being --- libcdoc.i | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libcdoc.i b/libcdoc.i index b25531ac..b34b421a 100644 --- a/libcdoc.i +++ b/libcdoc.i @@ -51,6 +51,9 @@ %ignore libcdoc::FileListConsumer; %ignore libcdoc::FileListSource; +// Ignore until there is straightfoward string_view translation +%ignore libcdoc::CDoc2; + %ignore libcdoc::CDocWriter::createWriter(int version, DataConsumer *dst, bool take_ownership, Configuration *conf, CryptoBackend *crypto, NetworkBackend *network); %ignore libcdoc::CDocWriter::createWriter(int version, std::ostream& ofs, Configuration *conf, CryptoBackend *crypto, NetworkBackend *network); %ignore libcdoc::CDocWriter::encrypt(MultiDataSource& src, const std::vector& recipients); From 3493f793a82758850ae82ed89037a6e52c6e96d1 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 27 Mar 2026 12:15:13 +0200 Subject: [PATCH 27/31] Remove commented-out code --- cdoc/CDoc.h | 2 +- cdoc/CDoc2Reader.cpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/cdoc/CDoc.h b/cdoc/CDoc.h index 2497eabd..a6500e2b 100644 --- a/cdoc/CDoc.h +++ b/cdoc/CDoc.h @@ -188,7 +188,7 @@ struct FileInfo { * * This is class (instead of namespace) to streamline Swig wrapping */ -struct CDOC_EXPORT CDoc2 { +struct CDOC_EXPORT CDoc2 final { /** * @brief Recipient types for machine-readable labels diff --git a/cdoc/CDoc2Reader.cpp b/cdoc/CDoc2Reader.cpp index daeb688d..3f828ba4 100644 --- a/cdoc/CDoc2Reader.cpp +++ b/cdoc/CDoc2Reader.cpp @@ -630,7 +630,6 @@ CDoc2Reader::CDoc2Reader(libcdoc::DataSource *src, bool take_ownership) LOG_ERROR("{}", last_error); return; } - //if (libcdoc::CDoc2Internal::LABEL.compare(0, libcdoc::CDoc2Internal::LABEL.size(), (const char *) in)) return; // Read 32-bit header length in big endian order uint8_t c[4]; From cb799439ede321673f086fe3e4f786ae779a0fb8 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 27 Mar 2026 12:29:05 +0200 Subject: [PATCH 28/31] Remove 'final' once more --- cdoc/CDoc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdoc/CDoc.h b/cdoc/CDoc.h index a6500e2b..2497eabd 100644 --- a/cdoc/CDoc.h +++ b/cdoc/CDoc.h @@ -188,7 +188,7 @@ struct FileInfo { * * This is class (instead of namespace) to streamline Swig wrapping */ -struct CDOC_EXPORT CDoc2 final { +struct CDOC_EXPORT CDoc2 { /** * @brief Recipient types for machine-readable labels From 90fd645de49aa504cd5ded8b73cf1b8823ee7887 Mon Sep 17 00:00:00 2001 From: Lauris Kaplinski Date: Fri, 27 Mar 2026 12:50:33 +0200 Subject: [PATCH 29/31] Made CDoc2 back to namespace --- cdoc/CDoc.h | 9 +-------- cdoc/CDoc2.h | 20 ++++++++++---------- cdoc/CDoc2Reader.cpp | 42 +++++++++++++++++++++--------------------- cdoc/CDoc2Writer.cpp | 38 +++++++++++++++++++------------------- cdoc/Lock.cpp | 8 ++++---- cdoc/Recipient.cpp | 6 +++--- cdoc/WinBackend.cpp | 2 +- 7 files changed, 59 insertions(+), 66 deletions(-) diff --git a/cdoc/CDoc.h b/cdoc/CDoc.h index 2497eabd..3da767a6 100644 --- a/cdoc/CDoc.h +++ b/cdoc/CDoc.h @@ -183,12 +183,7 @@ struct FileInfo { int64_t size; }; -/** - * @brief CDoc2-specific public values - * - * This is class (instead of namespace) to streamline Swig wrapping - */ -struct CDOC_EXPORT CDoc2 { +namespace CDoc2 { /** * @brief Recipient types for machine-readable labels @@ -216,8 +211,6 @@ struct CDOC_EXPORT CDoc2 { static constexpr std::string_view LBL_LAST_NAME = "last_name"; static constexpr std::string_view LBL_FIRST_NAME = "first_name"; static constexpr std::string_view LBL_CERT_SHA1 = "cert_sha1"; - - CDoc2() = delete; }; }; // namespace libcdoc diff --git a/cdoc/CDoc2.h b/cdoc/CDoc2.h index 9f7b3902..1be34d68 100644 --- a/cdoc/CDoc2.h +++ b/cdoc/CDoc2.h @@ -24,7 +24,7 @@ #include namespace libcdoc { -namespace CDoc2Internal { +namespace CDoc2 { static constexpr std::string_view LABEL = "CDOC\x02"; static constexpr std::string_view CEK = "CDOC20cek"; @@ -55,15 +55,15 @@ constexpr std::string_view LABELPREFIX{"data:"}; */ constexpr std::string_view LABELBASE64IND{";base64,"}; - /** - * @brief EID type values for machine-readable label - */ - static constexpr std::string_view eid_strs[] = { - CDoc2::TYPE_UNKNOWN, - CDoc2::TYPE_ID_CARD, - CDoc2::TYPE_DIGI_ID, - CDoc2::TYPE_DIGI_ID_E_RESIDENT - }; +/** + * @brief EID type values for machine-readable label + */ +static constexpr std::string_view eid_strs[] = { + CDoc2::TYPE_UNKNOWN, + CDoc2::TYPE_ID_CARD, + CDoc2::TYPE_DIGI_ID, + CDoc2::TYPE_DIGI_ID_E_RESIDENT +}; } // namespace CDoc2 } // namespace libcdoc diff --git a/cdoc/CDoc2Reader.cpp b/cdoc/CDoc2Reader.cpp index 3f828ba4..4e9b1199 100644 --- a/cdoc/CDoc2Reader.cpp +++ b/cdoc/CDoc2Reader.cpp @@ -47,19 +47,19 @@ using namespace libcdoc; // Get salt bitstring for HKDF expand method std::string -libcdoc::CDoc2Internal::getSaltForExpand(const std::string& label) +libcdoc::CDoc2::getSaltForExpand(const std::string& label) { std::ostringstream oss; - oss << libcdoc::CDoc2Internal::KEK << cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) << label; + oss << libcdoc::CDoc2::KEK << cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) << label; return oss.str(); } // Get salt bitstring for HKDF expand method std::string -libcdoc::CDoc2Internal::getSaltForExpand(const std::vector& key_material, const std::vector& rcpt_key) +libcdoc::CDoc2::getSaltForExpand(const std::vector& key_material, const std::vector& rcpt_key) { std::ostringstream oss; - oss << libcdoc::CDoc2Internal::KEK + oss << libcdoc::CDoc2::KEK << cdoc20::header::EnumNameFMKEncryptionMethod(cdoc20::header::FMKEncryptionMethod::XOR) << std::string_view((const char*)rcpt_key.data(), rcpt_key.size()) << std::string_view((const char*)key_material.data(), key_material.size()); @@ -149,7 +149,7 @@ CDoc2Reader::getFMK(std::vector& fmk, unsigned int lock_idx) if (lock.type == Lock::Type::PASSWORD) { // Password LOG_DBG("password"); - std::string info_str = libcdoc::CDoc2Internal::getSaltForExpand(lock.label); + std::string info_str = libcdoc::CDoc2::getSaltForExpand(lock.label); LOG_DBG("info: {}", toHex(info_str)); std::vector kek_pm; 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) { @@ -163,7 +163,7 @@ CDoc2Reader::getFMK(std::vector& fmk, unsigned int lock_idx) } else if (lock.type == Lock::Type::SYMMETRIC_KEY) { // Symmetric key LOG_DBG("symmetric"); - std::string info_str = libcdoc::CDoc2Internal::getSaltForExpand(lock.label); + std::string info_str = libcdoc::CDoc2::getSaltForExpand(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) { @@ -217,16 +217,16 @@ CDoc2Reader::getFMK(std::vector& fmk, unsigned int lock_idx) } } else { std::vector kek_pm; - int result = crypto->deriveHMACExtract(kek_pm, key_material, toUint8Vector(libcdoc::CDoc2Internal::KEKPREMASTER), 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); return result; } LOG_TRACE_KEY("Key kekPm: {}", kek_pm); - std::string info_str = libcdoc::CDoc2Internal::getSaltForExpand(key_material, lock.getBytes(Lock::Params::RCPT_KEY)); + 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::CDoc2Internal::KEY_LEN); + kek = libcdoc::Crypto::expand(kek_pm, info_str, libcdoc::CDoc2::KEY_LEN); } } else if (lock.type == Lock::Type::SHARE_SERVER) { /* SALT */ @@ -345,7 +345,7 @@ CDoc2Reader::getFMK(std::vector& fmk, unsigned int lock_idx) LOG_ERROR("{}", last_error); return libcdoc::CRYPTO_ERROR; } - std::vector hhk = libcdoc::Crypto::expand(fmk, libcdoc::CDoc2Internal::HMAC); + std::vector hhk = libcdoc::Crypto::expand(fmk, libcdoc::CDoc2::HMAC); LOG_TRACE_KEY("xor: {}", lock.encrypted_fmk); LOG_TRACE_KEY("fmk: {}", fmk); @@ -409,11 +409,11 @@ CDoc2Reader::beginDecryption(const std::vector& fmk) } } priv->_at_nonce = false; - std::vector cek = libcdoc::Crypto::expand(fmk, libcdoc::CDoc2Internal::CEK); + std::vector cek = libcdoc::Crypto::expand(fmk, libcdoc::CDoc2::CEK); LOG_TRACE_KEY("cek: {}", cek); - priv->dec = std::make_unique(*priv->_src, EVP_chacha20_poly1305(), cek, libcdoc::CDoc2Internal::NONCE_LEN); - std::vector aad = toUint8Vector(libcdoc::CDoc2Internal::PAYLOAD); + priv->dec = std::make_unique(*priv->_src, EVP_chacha20_poly1305(), cek, libcdoc::CDoc2::NONCE_LEN); + 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) { @@ -621,12 +621,12 @@ CDoc2Reader::CDoc2Reader(libcdoc::DataSource *src, bool take_ownership) setLastError(t_("Invalid CDoc 2.0 header")); - uint8_t in[libcdoc::CDoc2Internal::LABEL.size()]; - if (priv->_src->read(in, libcdoc::CDoc2Internal::LABEL.size()) != libcdoc::CDoc2Internal::LABEL.size()) { + uint8_t in[libcdoc::CDoc2::LABEL.size()]; + if (priv->_src->read(in, libcdoc::CDoc2::LABEL.size()) != libcdoc::CDoc2::LABEL.size()) { LOG_ERROR("{}", last_error); return; } - if (memcmp(libcdoc::CDoc2Internal::LABEL.data(), in, libcdoc::CDoc2Internal::LABEL.size())) { + if (memcmp(libcdoc::CDoc2::LABEL.data(), in, libcdoc::CDoc2::LABEL.size())) { LOG_ERROR("{}", last_error); return; } @@ -647,13 +647,13 @@ CDoc2Reader::CDoc2Reader(libcdoc::DataSource *src, bool take_ownership) LOG_ERROR("{}", last_error); return; } - priv->headerHMAC.resize(libcdoc::CDoc2Internal::KEY_LEN); - if (priv->_src->read(priv->headerHMAC.data(), libcdoc::CDoc2Internal::KEY_LEN) != libcdoc::CDoc2Internal::KEY_LEN) { + priv->headerHMAC.resize(libcdoc::CDoc2::KEY_LEN); + if (priv->_src->read(priv->headerHMAC.data(), libcdoc::CDoc2::KEY_LEN) != libcdoc::CDoc2::KEY_LEN) { LOG_ERROR("{}", last_error); return; } - priv->_nonce_pos = libcdoc::CDoc2Internal::LABEL.size() + 4 + header_len + libcdoc::CDoc2Internal::KEY_LEN; + priv->_nonce_pos = libcdoc::CDoc2::LABEL.size() + 4 + header_len + libcdoc::CDoc2::KEY_LEN; priv->_at_nonce = true; flatbuffers::Verifier verifier(priv->header_data.data(), priv->header_data.size()); @@ -686,12 +686,12 @@ CDoc2Reader::CDoc2Reader(libcdoc::DataSource *src, bool take_ownership) bool CDoc2Reader::isCDoc2File(libcdoc::DataSource *src) { - std::array in {}; + std::array in {}; if (src->read(in.data(), in.size()) != in.size()) { LOG_DBG("CDoc2Reader::isCDoc2File: Cannot read tag"); return false; } - if (libcdoc::CDoc2Internal::LABEL.compare(0, in.size(), (char *) in.data(), in.size())) { + if (libcdoc::CDoc2::LABEL.compare(0, in.size(), (char *) in.data(), in.size())) { LOG_DBG("CDoc2Reader::isCDoc2File: Invalid tag: {}", toHex(in)); return false; } diff --git a/cdoc/CDoc2Writer.cpp b/cdoc/CDoc2Writer.cpp index 349aa0f6..eceeeffe 100644 --- a/cdoc/CDoc2Writer.cpp +++ b/cdoc/CDoc2Writer.cpp @@ -49,9 +49,9 @@ CDoc2Writer::writeHeader(const std::vector &recipients) return libcdoc::WRONG_ARGUMENTS; } std::vector rnd; - if(auto rv = crypto->random(rnd, libcdoc::CDoc2Internal::KEY_LEN); rv < 0) + if(auto rv = crypto->random(rnd, libcdoc::CDoc2::KEY_LEN); rv < 0) return rv; - std::vector fmk = libcdoc::Crypto::extract(rnd, {libcdoc::CDoc2Internal::SALT.cbegin(), libcdoc::CDoc2Internal::SALT.cend()}); + std::vector fmk = libcdoc::Crypto::extract(rnd, {libcdoc::CDoc2::SALT.cbegin(), libcdoc::CDoc2::SALT.cend()}); std::fill(rnd.begin(), rnd.end(), 0); LOG_TRACE_KEY("fmk: {}", fmk); @@ -62,8 +62,8 @@ CDoc2Writer::writeHeader(const std::vector &recipients) return rv; } - auto hhk = libcdoc::Crypto::expand(fmk, libcdoc::CDoc2Internal::HMAC); - auto cek = libcdoc::Crypto::expand(fmk, libcdoc::CDoc2Internal::CEK); + auto hhk = libcdoc::Crypto::expand(fmk, libcdoc::CDoc2::HMAC); + auto cek = libcdoc::Crypto::expand(fmk, libcdoc::CDoc2::CEK); std::fill(fmk.begin(), fmk.end(), 0); LOG_TRACE_KEY("cek: {}", cek); LOG_TRACE_KEY("hhk: {}", hhk); @@ -75,16 +75,16 @@ CDoc2Writer::writeHeader(const std::vector &recipients) // FIXME: not big/little endian friendly uint8_t header_len[] {uint8_t(hs >> 24), uint8_t((hs >> 16) & 0xff), uint8_t((hs >> 8) & 0xff), uint8_t(hs & 0xff)}; - dst->write((const uint8_t *) libcdoc::CDoc2Internal::LABEL.data(), libcdoc::CDoc2Internal::LABEL.size()); + dst->write((const uint8_t *) libcdoc::CDoc2::LABEL.data(), libcdoc::CDoc2::LABEL.size()); dst->write((const uint8_t *) &header_len, 4); dst->write(header.data(), header.size()); dst->write(headerHMAC.data(), headerHMAC.size()); std::vector nonce; - crypto->random(nonce, libcdoc::CDoc2Internal::NONCE_LEN); + crypto->random(nonce, libcdoc::CDoc2::NONCE_LEN); LOG_TRACE_KEY("nonce: {}", nonce); auto cipher = std::make_unique(*dst, EVP_chacha20_poly1305(), Crypto::Key(std::move(cek), nonce)); - std::vector aad(libcdoc::CDoc2Internal::PAYLOAD.cbegin(), libcdoc::CDoc2Internal::PAYLOAD.cend()); + std::vector aad(libcdoc::CDoc2::PAYLOAD.cbegin(), libcdoc::CDoc2::PAYLOAD.cend()); aad.insert(aad.end(), header.cbegin(), header.cend()); aad.insert(aad.end(), headerHMAC.cbegin(), headerHMAC.cend()); if(auto rv = cipher->writeAAD(aad); rv < 0) @@ -198,7 +198,7 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vector> fb_rcpts; - std::vector xor_key(libcdoc::CDoc2Internal::KEY_LEN); + std::vector xor_key(libcdoc::CDoc2::KEY_LEN); for (unsigned int rcpt_idx = 0; rcpt_idx < recipients.size(); rcpt_idx++) { const libcdoc::Recipient& rcpt = recipients.at(rcpt_idx); if (rcpt.isPKI()) { @@ -223,7 +223,7 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vectorrandom(kek, libcdoc::CDoc2Internal::KEY_LEN); + crypto->random(kek, libcdoc::CDoc2::KEY_LEN); if (libcdoc::Crypto::xor_data(xor_key, fmk, kek) != libcdoc::OK) { setLastError("Internal error"); LOG_ERROR("{}", last_error); @@ -267,8 +267,8 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vector sharedSecret = libcdoc::Crypto::deriveSharedSecret(ephKey.get(), publicKey.get()); key_material = libcdoc::Crypto::toPublicKeyDer(ephKey.get()); - std::vector kekPm = libcdoc::Crypto::extract(sharedSecret, std::vector(libcdoc::CDoc2Internal::KEKPREMASTER.cbegin(), libcdoc::CDoc2Internal::KEKPREMASTER.cend())); - std::string info_str = libcdoc::CDoc2Internal::getSaltForExpand(key_material, rcpt.rcpt_key); + std::vector kekPm = libcdoc::Crypto::extract(sharedSecret, std::vector(libcdoc::CDoc2::KEKPREMASTER.cbegin(), libcdoc::CDoc2::KEKPREMASTER.cend())); + std::string info_str = libcdoc::CDoc2::getSaltForExpand(key_material, rcpt.rcpt_key); kek = libcdoc::Crypto::expand(kekPm, info_str, fmk.size()); if (libcdoc::Crypto::xor_data(xor_key, fmk, kek) != libcdoc::OK) { @@ -302,16 +302,16 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vector kek_pm(libcdoc::CDoc2Internal::KEY_LEN); + std::string info_str = libcdoc::CDoc2::getSaltForExpand(rcpt.getLabel({})); + std::vector kek_pm(libcdoc::CDoc2::KEY_LEN); std::vector salt; - int64_t result = crypto->random(salt, libcdoc::CDoc2Internal::KEY_LEN); + int64_t result = crypto->random(salt, libcdoc::CDoc2::KEY_LEN); if (result < 0) { setLastError(crypto->getLastErrorStr(result)); return result; } std::vector pw_salt; - result = crypto->random(pw_salt, libcdoc::CDoc2Internal::KEY_LEN); + result = crypto->random(pw_salt, libcdoc::CDoc2::KEY_LEN); if (result < 0) { setLastError(crypto->getLastErrorStr(result)); return result; @@ -321,7 +321,7 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vectorgetLastErrorStr(result)); return result; } - std::vector kek = libcdoc::Crypto::expand(kek_pm, info_str, libcdoc::CDoc2Internal::KEY_LEN); + std::vector kek = libcdoc::Crypto::expand(kek_pm, info_str, libcdoc::CDoc2::KEY_LEN); LOG_DBG("Label: {}", rcpt.label); LOG_DBG("KDF iter: {}", rcpt.kdf_iter); @@ -368,11 +368,11 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vector key_material_salt; - crypto->random(key_material_salt, libcdoc::CDoc2Internal::KEY_LEN); + crypto->random(key_material_salt, libcdoc::CDoc2::KEY_LEN); //KeyMaterial_i = CSRNG(256) std::vector key_material; - crypto->random(key_material, libcdoc::CDoc2Internal::KEY_LEN); + crypto->random(key_material, libcdoc::CDoc2::KEY_LEN); //KEK_i_pm = HKDF_Extract(KeyMaterialSalt_i, KeyMaterial_i) std::vector kek_pm = libcdoc::Crypto::extract(key_material_salt, key_material); @@ -394,7 +394,7 @@ CDoc2Writer::buildHeader(std::vector& header, const std::vector> kek_shares(N_SHARES); for (int i = 1; i < N_SHARES; i++) { // KEK_i_share_j = CSRNG(256) - crypto->random(kek_shares[i], libcdoc::CDoc2Internal::KEY_LEN); + crypto->random(kek_shares[i], libcdoc::CDoc2::KEY_LEN); } // KEK_i_share_1 = XOR(KEK_i, KEK_i_share_2, KEK_i_share_3,..., KEK_i_share_n) kek_shares[0] = std::move(kek); diff --git a/cdoc/Lock.cpp b/cdoc/Lock.cpp index f61d6f2d..d5dd3614 100644 --- a/cdoc/Lock.cpp +++ b/cdoc/Lock.cpp @@ -59,12 +59,12 @@ Lock::parseLabel(const std::string& label) { std::map parsed_label; // Check if provided label starts with the machine generated label prefix. - if (!label.starts_with(CDoc2Internal::LABELPREFIX)) + if (!label.starts_with(CDoc2::LABELPREFIX)) { return parsed_label; } - std::string label_wo_prefix(label.substr(CDoc2Internal::LABELPREFIX.size())); + std::string label_wo_prefix(label.substr(CDoc2::LABELPREFIX.size())); // Label to be processed std::string label_to_prcss; @@ -72,7 +72,7 @@ Lock::parseLabel(const std::string& label) // We ignore mediatype part // Check, if the label is Base64 encoded - auto base64IndPos = label_wo_prefix.find(CDoc2Internal::LABELBASE64IND); + auto base64IndPos = label_wo_prefix.find(CDoc2::LABELBASE64IND); if (base64IndPos == std::string::npos) { if (label_wo_prefix.starts_with(",")) { @@ -83,7 +83,7 @@ Lock::parseLabel(const std::string& label) } else { - std::string base64_label(label_wo_prefix.substr(base64IndPos + CDoc2Internal::LABELBASE64IND.size())); + std::string base64_label(label_wo_prefix.substr(base64IndPos + CDoc2::LABELBASE64IND.size())); label_to_prcss = jwt::base::decode(base64_label); } diff --git a/cdoc/Recipient.cpp b/cdoc/Recipient.cpp index 91fd3a43..6929ec07 100644 --- a/cdoc/Recipient.cpp +++ b/cdoc/Recipient.cpp @@ -132,8 +132,8 @@ buildLabel(std::ostream& ofs, std::string_view type, const std::map