From 3d2e3a99994fbec20b24b452f20e70f46d8d6228 Mon Sep 17 00:00:00 2001 From: Charles Cabergs Date: Tue, 9 Dec 2025 18:32:39 +0100 Subject: [PATCH 1/2] Add curve functions wrappers --- tests/CMakeLists.txt | 1 + tests/curve.cpp | 29 ++++++++++++++++++++++++ zmq.hpp | 52 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 tests/curve.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3821c5c..3adfcc4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -31,6 +31,7 @@ add_executable( monitor.cpp utilities.cpp timers.cpp + curve.cpp ) target_include_directories(unit_tests PUBLIC ${CATCH_MODULE_PATH}) diff --git a/tests/curve.cpp b/tests/curve.cpp new file mode 100644 index 0000000..4d30663 --- /dev/null +++ b/tests/curve.cpp @@ -0,0 +1,29 @@ +#include +#include + +TEST_CASE("curve keypair", "[curve]") +{ + auto [public_key, secret_key] = zmq::curve::keypair(); + CHECK(!public_key.empty()); + CHECK(!secret_key.empty()); +} + +TEST_CASE("curve public_", "[curve]") +{ + auto secret_key = "D:)Q[IlAW!ahhC2ac:9*A}h:p?([4%wOTJ%JR%cs"; + auto public_key = zmq::curve::public_(secret_key); + CHECK(public_key == "Yne@$w-vo data{1,2,3,4,5,6,7,8}; + auto encoded = zmq::curve::z85::encode(data); + CHECK(encoded == "0rJua1Qkhq"); +} + +TEST_CASE("curve z85 decode", "[curve]") +{ + auto decoded = zmq::curve::z85::decode("0rJua1Qkhq"); + CHECK(decoded == std::vector{1,2,3,4,5,6,7,8}); +} diff --git a/zmq.hpp b/zmq.hpp index ad0509e..a4afd4e 100644 --- a/zmq.hpp +++ b/zmq.hpp @@ -107,6 +107,7 @@ #include #include +#include #include #include @@ -2874,6 +2875,57 @@ class timers #endif // defined(ZMQ_CPP11) && defined(ZMQ_HAVE_TIMERS) +namespace curve { + +inline std::pair keypair() +{ + char public_key_buffer[41]; + char secret_key_buffer[41]; + int rc = zmq_curve_keypair(public_key_buffer, secret_key_buffer); + if (rc == -1) + throw zmq::error_t(); + return std::pair{public_key_buffer, secret_key_buffer}; +} + +inline std::string public_(const std::string& secret) +{ + if (secret.size() != 40) + throw std::runtime_error("Invalid secret string size"); + char public_key_buffer[41]; + int rc = zmq_curve_public(public_key_buffer, secret.c_str()); + if (rc == -1) + throw zmq::error_t(); + return public_key_buffer; +} + +namespace z85 { + +inline std::string encode(const std::vector& data) +{ + size_t buffer_size = static_cast(data.size()) * 1.25 + 1; + char *buffer = new char[buffer_size]; + auto *result = zmq_z85_encode(buffer, data.data(), data.size()); + if (result == nullptr) + throw zmq::error_t(); + std::string dest(result); + delete [] buffer; + return dest; +} + +inline std::vector decode(const std::string& encoded) +{ + size_t dest_size = static_cast(encoded.size()) * 0.8; + std::vector dest(dest_size); + auto *result = zmq_z85_decode(dest.data(), encoded.c_str()); + if (result == nullptr) + throw zmq::error_t(); + return dest; +} + +} + +} + } // namespace zmq #endif // __ZMQ_HPP_INCLUDED__ From d80e753659aceb3fc9a984992a33827b7fd565e8 Mon Sep 17 00:00:00 2001 From: Charles Cabergs Date: Sat, 13 Dec 2025 17:38:42 +0100 Subject: [PATCH 2/2] Remove zmq::curve and zmq::curver::z85 namespaces to fit the library style, Fix the z85_encode memory leak on an exception thrown, Change encode/decode size computation to stick only with integral numbers --- .github/workflows/ci.yml | 8 ++++---- tests/curve.cpp | 17 +++++++++-------- zmq.hpp | 34 +++++++++++++--------------------- 3 files changed, 26 insertions(+), 33 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b310b27..33df3a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,8 +22,8 @@ jobs: # older libzmq and without draft - os: "ubuntu-22.04" cppstd: "11" - cc: "gcc-9" - cxx: "g++-9" + cc: "gcc-11" + cxx: "g++-11" drafts: "OFF" libzmq: "4.2.0" libzmqbuild: "pkgconfig" @@ -38,8 +38,8 @@ jobs: # coverage (gcc version should match gcov version) - os: "ubuntu-22.04" cppstd: "17" - cc: "gcc-9" - cxx: "g++-9" + cc: "gcc-11" + cxx: "g++-11" drafts: "ON" libzmq: "4.3.5" libzmqbuild: "cmake" diff --git a/tests/curve.cpp b/tests/curve.cpp index 4d30663..8c93d80 100644 --- a/tests/curve.cpp +++ b/tests/curve.cpp @@ -1,29 +1,30 @@ #include #include -TEST_CASE("curve keypair", "[curve]") +TEST_CASE("curve_keypair", "[curve]") { - auto [public_key, secret_key] = zmq::curve::keypair(); + auto [public_key, secret_key] = zmq::curve_keypair(); CHECK(!public_key.empty()); CHECK(!secret_key.empty()); } -TEST_CASE("curve public_", "[curve]") +TEST_CASE("curve_public_", "[curve]") { auto secret_key = "D:)Q[IlAW!ahhC2ac:9*A}h:p?([4%wOTJ%JR%cs"; - auto public_key = zmq::curve::public_(secret_key); + auto public_key = zmq::curve_public(secret_key); CHECK(public_key == "Yne@$w-vo data{1,2,3,4,5,6,7,8}; - auto encoded = zmq::curve::z85::encode(data); + auto encoded = zmq::z85_encode(data); + CHECK(encoded.size() == std::string("0rJua1Qkhq").size()); CHECK(encoded == "0rJua1Qkhq"); } -TEST_CASE("curve z85 decode", "[curve]") +TEST_CASE("z85_decode", "[curve]") { - auto decoded = zmq::curve::z85::decode("0rJua1Qkhq"); + auto decoded = zmq::z85_decode("0rJua1Qkhq"); CHECK(decoded == std::vector{1,2,3,4,5,6,7,8}); } diff --git a/zmq.hpp b/zmq.hpp index a4afd4e..0a21358 100644 --- a/zmq.hpp +++ b/zmq.hpp @@ -540,7 +540,7 @@ class message_t throw error_t(); memcpy(data(), data_, size_); } - + void rebuild(const std::string &str) { rebuild(str.data(), str.size()); @@ -2493,7 +2493,7 @@ class monitor_t (void) addr_; } - protected: + protected: bool process_event(short events) { zmq::message_t eventMsg; @@ -2875,9 +2875,7 @@ class timers #endif // defined(ZMQ_CPP11) && defined(ZMQ_HAVE_TIMERS) -namespace curve { - -inline std::pair keypair() +inline std::pair curve_keypair() { char public_key_buffer[41]; char secret_key_buffer[41]; @@ -2887,7 +2885,7 @@ inline std::pair keypair() return std::pair{public_key_buffer, secret_key_buffer}; } -inline std::string public_(const std::string& secret) +inline std::string curve_public(const std::string& secret) { if (secret.size() != 40) throw std::runtime_error("Invalid secret string size"); @@ -2898,23 +2896,21 @@ inline std::string public_(const std::string& secret) return public_key_buffer; } -namespace z85 { - -inline std::string encode(const std::vector& data) +inline std::string z85_encode(const std::vector& data) { - size_t buffer_size = static_cast(data.size()) * 1.25 + 1; - char *buffer = new char[buffer_size]; - auto *result = zmq_z85_encode(buffer, data.data(), data.size()); + size_t buffer_size = data.size() * size_t{6} / size_t{5} + 1; + std::string buffer(buffer_size, '\0'); + auto *result = zmq_z85_encode(buffer.data(), data.data(), data.size()); if (result == nullptr) throw zmq::error_t(); - std::string dest(result); - delete [] buffer; - return dest; + while (buffer.back() == '\0') + buffer.pop_back(); + return buffer; } -inline std::vector decode(const std::string& encoded) +inline std::vector z85_decode(const std::string& encoded) { - size_t dest_size = static_cast(encoded.size()) * 0.8; + size_t dest_size = encoded.size() * size_t{4} / size_t{5}; std::vector dest(dest_size); auto *result = zmq_z85_decode(dest.data(), encoded.c_str()); if (result == nullptr) @@ -2922,10 +2918,6 @@ inline std::vector decode(const std::string& encoded) return dest; } -} - -} - } // namespace zmq #endif // __ZMQ_HPP_INCLUDED__