diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 4e3ae6aa..4a1f1ce8 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -1,13 +1,26 @@ { + "env": { + "includePath": [ + "${workspaceFolder}/include", + "${workspaceFolder}/dist/*/build/*-extern-prefix/src/**", + "${workspaceFolder}/examples/**", + "${workspaceFolder}/tests/**" + ], + "defines": [ + "STORED_HAVE_ZMQ", + "STORED_HAVE_AES", + "STORED_HAVE_HEATSHRINK", + "AES256" + ] + }, "configurations": [ { "name": "Linux", "includePath": [ - "${workspaceFolder}/include", - "${workspaceFolder}/**" + "${env:includePath}" ], "defines": [ - "STORED_HAVE_ZMQ" + "${env:defines}" ], "compilerPath": "/usr/bin/clang", "cStandard": "c17", @@ -17,11 +30,10 @@ { "name": "Win32", "includePath": [ - "${workspaceFolder}/include", - "${workspaceFolder}/**" + "${env:includePath}" ], "defines": [ - "STORED_HAVE_ZMQ" + "${env:defines}" ], "cStandard": "c17", "cppStandard": "c++17" @@ -29,11 +41,10 @@ { "name": "Mac", "includePath": [ - "${workspaceFolder}/include", - "${workspaceFolder}/**" + "${env:includePath}" ], "defines": [ - "STORED_HAVE_ZMQ" + "${env:defines}" ], "compilerPath": "/usr/bin/clang", "cStandard": "c17", diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a34be5f9..847925d9 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -24,9 +24,27 @@ The format is based on `Keep a Changelog`_, and this project adheres to Added ````` +... + +.. _Unreleased: https://github.com/DEMCON/libstored/compare/v2.1.0...HEAD + + + +`2.1.0`_ - 2025-12-05 +--------------------- + +Added +````` + - Operations such as ``-=`` and ``++`` for store variables in C++. - YAML export of store meta-data. - ``stored::MuxLayer`` to multiplex multiple protocol layers over a single connection. +- ``stored::Aes256Layer`` and ``libstored.protocol.Aes256Layer`` for encrypted communication using + AES-256 CTR. +- Allow adding a protocol layer stack to ``ZmqClient`` on top of the ZeroMQ socket. If putting + the ``Aes256Layer`` in the stack, encrypted debug communication is enabled with the target. +- ``lossy_sync`` example that shows how to use the Synchronizer over a lossy protocol, and how to + use encryption in both the Synchronizer and Debugger stacks. Changed ``````` @@ -40,7 +58,7 @@ Fixed - Init value of CRC32 in Python ``libstored.protocol.Crc32Layer``. -.. _Unreleased: https://github.com/DEMCON/libstored/compare/v2.0.0...HEAD +.. _2.1.0: https://github.com/DEMCON/libstored/releases/tag/v2.1.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c63d20e..08f998b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -160,6 +160,12 @@ if(LIBSTORED_HAVE_ZTH) find_package(Zth REQUIRED) endif() +option(LIBSTORED_HAVE_AES "Use AES" ON) + +if(LIBSTORED_HAVE_AES) + find_package(TinyAES REQUIRED) +endif() + set(LIBSTORED_CLANG_TIDY_DEFAULT OFF) if(${CMAKE_VERSION} VERSION_GREATER "3.6.0") diff --git a/README.rst b/README.rst index 56645238..ea9207bf 100644 --- a/README.rst +++ b/README.rst @@ -216,8 +216,8 @@ device. However, once you implemented this data transport, you can access the store, and observe and manipulate it using an Embedded Debugger (PC) client. Moreover, the protocol supports arbitrary streams (like stdout) from the application to the client, and has high-speed tracing of store variables. These -streams are optionally heatshrink_ compressed. libstored provides Python -classes for your custom scripts, a CLI and GUI interface. +streams are optionally heatshrink_ compressed, and AES-256 encrypted via tiny-AES-c_. +libstored provides Python classes for your custom scripts, a CLI and GUI interface. Your application can have one store with one debugging interface, but also multiple stores with one debugging interface, or one store with multiple @@ -457,7 +457,7 @@ generate stuff for you. This is how to integrate it in your project: Before including ``libstored``, you can specify several options (see ``cmake/libstored.cmake``), such as enabling ASan or clang-tidy. - Especially the library dependencies (ZeroMQ, Zth, heatshrink) are + Especially the library dependencies (ZeroMQ, Zth, heatshrink, tiny-AES-c) are relevant to consider. For example, to enable ZeroMQ: .. code:: cmake @@ -514,7 +514,7 @@ approach. In that case: 3. In your project, call ``find_package(Libstored)``, while having the generated ``FindLibstored.cmake`` in your ``CMAKE_MODULE_PATH``. - ``Libstored`` accepts ``ZeroMQ``, ``Zth``, ``Heatshrink`` as ``COMPONENTS``. + ``Libstored`` accepts ``ZeroMQ``, ``Zth``, ``Heatshrink``, and ``AES`` as ``COMPONENTS``. Setting these enables integration of these libraries with libstored. When possible, they are taken from your host system, or built from source. If you want more control over these libraries, you can also use the mechanism @@ -540,6 +540,7 @@ License, v. 2.0, as specified in LICENSE. This project complies to `REUSE`_. .. _documentation: https://demcon.github.io/libstored .. _8_sync: https://github.com/DEMCON/libstored/tree/main/examples/8_sync .. _heatshrink: https://github.com/atomicobject/heatshrink +.. _tiny-AES-c: https://github.com/kokke/tiny-AES-c .. _Kst: https://kst-plot.kde.org/ .. _python: https://github.com/DEMCON/libstored/tree/main/python .. _REUSE: https://reuse.software/ diff --git a/cmake/FindLibstored.cmake.tmpl b/cmake/FindLibstored.cmake.tmpl index f2ca2d77..a4f5cdc1 100644 --- a/cmake/FindLibstored.cmake.tmpl +++ b/cmake/FindLibstored.cmake.tmpl @@ -36,6 +36,9 @@ if(EXISTS ${LIBSTORED_SOURCE_DIR}/cmake/libstored.cmake) elseif("${c}" STREQUAL "Heatshrink") find_package(Heatshrink ${_req}) set(LIBSTORED_HAVE_HEATSHRINK ON CACHE INTERNAL "Enable heatshrink" FORCE) + elseif("${c}" STREQUAL "AES") + find_package(TinyAES ${_req}) + set(LIBSTORED_HAVE_AES ON CACHE INTERNAL "Enable AES" FORCE) endif() endforeach() diff --git a/cmake/LibstoredStoresConfig.cmake.in b/cmake/LibstoredStoresConfig.cmake.in index 46a96c3c..bbd678db 100644 --- a/cmake/LibstoredStoresConfig.cmake.in +++ b/cmake/LibstoredStoresConfig.cmake.in @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2020-2023 Jochem Rutgers +# SPDX-FileCopyrightText: 2020-2025 Jochem Rutgers # # SPDX-License-Identifier: MPL-2.0 @@ -47,6 +47,7 @@ endif() include(${LIBSTORED_STORES_CMAKE_PATH}/libstored.cmake OPTIONAL) include(${LIBSTORED_STORES_CMAKE_PATH}/heatshrink.cmake OPTIONAL) +include(${LIBSTORED_STORES_CMAKE_PATH}/tinyaes.cmake OPTIONAL) find_package(Zth) file(GLOB _stores ${LIBSTORED_STORES_CMAKE_PATH}/*Store.cmake) diff --git a/cmake/libstored.cmake b/cmake/libstored.cmake index 5a74387a..b5a222b7 100644 --- a/cmake/libstored.cmake +++ b/cmake/libstored.cmake @@ -34,6 +34,10 @@ option(LIBSTORED_HAVE_LIBZMQ "Use libzmq" OFF) # from source. option(LIBSTORED_HAVE_ZTH "Use Zth" OFF) +# When enabled, TinyAES is used for AES-256 CTR. It is searched via find_package(TinyAES). When +# provided by the system, make sure it is configured for AES-256 CTR mode. +option(LIBSTORED_HAVE_AES "Use AES-256 CTR from TinyAES" OFF) + # ################################################################################################## # Prepare environment @@ -198,6 +202,7 @@ Relationship: SPDXRef-compiler BUILD_DEPENDENCY_OF SPDXRef-libstored ${LIBSTORED_SOURCE_DIR}/include/stored ${LIBSTORED_SOURCE_DIR}/include/stored.h ${LIBSTORED_SOURCE_DIR}/include/stored_config.h + ${LIBSTORED_SOURCE_DIR}/include/libstored/aes.h ${LIBSTORED_SOURCE_DIR}/include/libstored/allocator.h ${LIBSTORED_SOURCE_DIR}/include/libstored/compress.h ${LIBSTORED_SOURCE_DIR}/include/libstored/config.h @@ -212,6 +217,7 @@ Relationship: SPDXRef-compiler BUILD_DEPENDENCY_OF SPDXRef-libstored ${LIBSTORED_SOURCE_DIR}/include/libstored/util.h ${LIBSTORED_SOURCE_DIR}/include/libstored/version.h ${LIBSTORED_SOURCE_DIR}/include/libstored/zmq.h + ${LIBSTORED_SOURCE_DIR}/src/aes.cpp ${LIBSTORED_SOURCE_DIR}/src/compress.cpp ${LIBSTORED_SOURCE_DIR}/src/directory.cpp ${LIBSTORED_SOURCE_DIR}/src/debugger.cpp @@ -403,7 +409,7 @@ Relationship: SPDXRef-libstored DEPENDS_ON SPDXRef-libzmq target_compile_definitions( ${LIBSTORED_LIB_TARGET} PUBLIC -DSTORED_HAVE_HEATSHRINK=1 ) - target_link_libraries(${LIBSTORED_LIB_TARGET} PUBLIC heatshrink) + target_link_libraries(${LIBSTORED_LIB_TARGET} PRIVATE heatshrink) set(_fields) @@ -442,6 +448,46 @@ Relationship: SPDXRef-libstored DEPENDS_ON SPDXRef-heatshrink ) endif() + if(LIBSTORED_HAVE_AES) + target_compile_definitions(${LIBSTORED_LIB_TARGET} PUBLIC -DSTORED_HAVE_AES=1) + target_link_libraries(${LIBSTORED_LIB_TARGET} PRIVATE tinyaes) + + set(_fields) + + if("${TinyAES_VERSION}" STREQUAL "") + set(_fields + "${_fields} +PackageVersion: preinstalled +PackageDownloadLocation: NOASSERTION +ExternalRef: PACKAGE-MANAGER purl pkg:github/kokke/tiny-AES-c" + ) + else() + set(_fields + "${_fields} +PackageVersion: ${TinyAES_VERSION} +PackageDownloadLocation: https://github.com/kokke/tiny-AES-c/commit/${TinyAES_VERSION} +ExternalRef: PACKAGE-MANAGER purl pkg:github/kokke/tiny-AES-c@${TinyAES_VERSION}" + ) + endif() + + file( + APPEND "${LIBSTORED_LIB_SBOM_CMAKE}" + " + file(APPEND \"${LIBSTORED_LIB_DESTINATION}/doc/sbom.spdx\" \" +PackageName: TinyAES +SPDXID: SPDXRef-TinyAES${_fields} +PackageHomePage: https://github.com/kokke/tiny-AES-c +FilesAnalyzed: false +PackageLicenseConcluded: Unlicense +PackageLicenseDeclared: Unlicense +PackageSummary: This is a small and portable implementation of the AES ECB, CTR and CBC encryption algorithms written in C. +PrimaryPackagePurpose: LIBRARY +Relationship: SPDXRef-libstored DEPENDS_ON SPDXRef-TinyAES +\") + " + ) + endif() + set(DO_CLANG_TIDY "") if(${CMAKE_VERSION} VERSION_GREATER "3.6.0") @@ -621,12 +667,13 @@ function(libstored_copy_dlls target) if(WIN32) get_target_property(target_type ${target} TYPE) get_property(LIBSTORED_RUNTIME_LIBS GLOBAL PROPERTY LIBSTORED_RUNTIME_LIBS) - if(target_type STREQUAL "EXECUTABLE" AND NOT "${LIBSTORED_RUNTIME_LIBS}" STREQUAL "") + if(target_type STREQUAL "EXECUTABLE" AND NOT "${LIBSTORED_RUNTIME_LIBS}" STREQUAL + "" + ) add_custom_command( TARGET ${target} PRE_LINK - COMMAND - ${CMAKE_COMMAND} -E copy ${LIBSTORED_RUNTIME_LIBS} + COMMAND ${CMAKE_COMMAND} -E copy ${LIBSTORED_RUNTIME_LIBS} $/ VERBATIM ) diff --git a/dist/common/FindHeatshrink.cmake b/dist/common/FindHeatshrink.cmake index 3ba459d9..bc8a2d95 100644 --- a/dist/common/FindHeatshrink.cmake +++ b/dist/common/FindHeatshrink.cmake @@ -50,11 +50,6 @@ set_source_files_properties(${heatshrink_src} PROPERTIES GENERATED 1) if(MSVC) target_compile_options(heatshrink PRIVATE /W1) - if(CMAKE_BUILD_TYPE STREQUAL "Debug") - target_compile_options(heatshrink PUBLIC /MTd) - else() - target_compile_options(heatshrink PUBLIC /MT) - endif() endif() target_include_directories( diff --git a/dist/common/FindTinyAES.cmake b/dist/common/FindTinyAES.cmake new file mode 100644 index 00000000..5f005260 --- /dev/null +++ b/dist/common/FindTinyAES.cmake @@ -0,0 +1,69 @@ +# SPDX-FileCopyrightText: 2020-2025 Jochem Rutgers +# +# SPDX-License-Identifier: MIT + +cmake_policy(VERSION 3.10) + +include(ExternalProject) +include(GNUInstallDirs) +find_package(Git) + +if("${TINYAES_GIT_URL}" STREQUAL "") + if(DEFINED ENV{LIBSTORED_GIT_CACHE}) + set(TINYAES_GIT_URL $ENV{LIBSTORED_GIT_CACHE}/tinyaes) + else() + set(TINYAES_GIT_URL "https://github.com/kokke/tiny-AES-c.git") + endif() +endif() + +set(TinyAES_VERSION 23856752fbd139da0b8ca6e471a13d5bcc99a08d) + +ExternalProject_Add( + tinyaes-extern + GIT_REPOSITORY ${TINYAES_GIT_URL} + GIT_TAG ${TinyAES_VERSION} + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + UPDATE_COMMAND ${CMAKE_COMMAND} -E chdir ${GIT_EXECUTABLE} checkout -- . + LOG_CONFIGURE 0 + LOG_BUILD 0 + LOG_TEST 0 + LOG_INSTALL 0 +) + +ExternalProject_Get_Property(tinyaes-extern SOURCE_DIR) + +# The source files are considered generated files. Upon a clean, they are removed. Hence the +# UPDATE_COMMAND to recover them. +add_library(tinyaes STATIC ${SOURCE_DIR}/aes.c) +set_target_properties(tinyaes PROPERTIES PUBLIC_HEADER "${SOURCE_DIR}/aes.h") +add_dependencies(tinyaes tinyaes-extern) + +get_target_property(tinyaes_src tinyaes SOURCES) +set_source_files_properties(${tinyaes_src} PROPERTIES GENERATED 1) + +if(MSVC) + target_compile_options(tinyaes PRIVATE /W1) +endif() + +target_include_directories( + tinyaes PUBLIC $ $ +) + +target_compile_definitions(tinyaes PUBLIC AES256=1 CTR=1 CBC=0 ECB=0) + +install( + TARGETS tinyaes + EXPORT tinyaes + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) + +if(WIN32) + install(EXPORT tinyaes DESTINATION CMake) +else() + install(EXPORT tinyaes DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/libstored/cmake) +endif() + +set(TinyAES_FOUND 1) diff --git a/dist/common/build.sh b/dist/common/build.sh index 650f0db8..5e518fe4 100644 --- a/dist/common/build.sh +++ b/dist/common/build.sh @@ -1,6 +1,6 @@ #!/bin/bash -# SPDX-FileCopyrightText: 2020-2024 Jochem Rutgers +# SPDX-FileCopyrightText: 2020-2025 Jochem Rutgers # # SPDX-License-Identifier: MPL-2.0 @@ -18,19 +18,25 @@ trap gotErr ERR function show_help { echo -e "Usage: $0 [...] [--] []\n" echo "where opt is:" - echo " Debug RelWithDebInfo Release" - echo " Set CMAKE_BUILD_TYPE to this value" - echo " C++98 C++03 C++11 C++14 C++17 C++20" - echo " Set the C++ standard" - echo " conf Configure only, don't build" - echo " dev Enable development-related options" - echo " test Enable building and running tests" - echo " zmq Enable ZeroMQ integration" - echo " nozmq Disable ZeroMQ integration" - echo " zth Enable Zth integration" - echo " fuzz Enable fuzzing with AFL++" - echo " gcov Enable gcov/lcov" - echo " clean Do a clean build" + echo " Debug RelWithDebInfo Release" + echo " Set CMAKE_BUILD_TYPE to this value" + echo " C++98 C++03 C++11 C++14 C++17 C++20" + echo " Set the C++ standard" + echo " conf Configure only, don't build" + echo " dev Enable development-related options" + echo " test Enable building and running tests" + echo " zmq Enable ZeroMQ integration" + echo " nozmq Disable ZeroMQ integration" + echo " heatshrink Enable Heatshrink integration" + echo " noheatshrink Disable Heatshrink integration" + echo " aes Enable TinyAES integration" + echo " noaes Disable TinyAES integration" + echo " san Enable sanitizers" + echo " nosan Disable sanitizers" + echo " zth Enable Zth integration" + echo " fuzz Enable fuzzing with AFL++" + echo " gcov Enable gcov/lcov" + echo " clean Do a clean build" exit 2 } @@ -99,6 +105,10 @@ while [[ ! -z ${1:-} ]]; do cmake_opts="${cmake_opts} -DLIBSTORED_HAVE_HEATSHRINK=ON";; noheatshrink) cmake_opts="${cmake_opts} -DLIBSTORED_HAVE_HEATSHRINK=OFF";; + aes) + cmake_opts="${cmake_opts} -DLIBSTORED_HAVE_AES=ON";; + noaes) + cmake_opts="${cmake_opts} -DLIBSTORED_HAVE_AES=OFF";; san) cmake_opts="${cmake_opts} -DLIBSTORED_ENABLE_ASAN=ON -DLIBSTORED_ENABLE_LSAN=ON -DLIBSTORED_ENABLE_UBSAN=ON";; nosan) diff --git a/dist/common/requirements-minimal.txt b/dist/common/requirements-minimal.txt index bd97a293..e697eb88 100644 --- a/dist/common/requirements-minimal.txt +++ b/dist/common/requirements-minimal.txt @@ -10,6 +10,7 @@ jinja2 matplotlib natsort platformdirs +pycryptodome pyserial >= 3.1 pyzmq textx diff --git a/dist/ubuntu/test_config.sh b/dist/ubuntu/test_config.sh index a47e1a23..b2d5871e 100755 --- a/dist/ubuntu/test_config.sh +++ b/dist/ubuntu/test_config.sh @@ -38,6 +38,8 @@ config Release nodev C++14 test config Release nodev C++17 test config Debug nodev noheatshrink test config Release noheatshrink test +config Debug nodev noaes test +config Release noaes test config Debug nodev noexamples test config Release nodev noexamples test config Debug nodev san zth test diff --git a/dist/win32/build.cmd b/dist/win32/build.cmd index 03fd61eb..db544b3a 100644 --- a/dist/win32/build.cmd +++ b/dist/win32/build.cmd @@ -18,6 +18,7 @@ set do_build=1 set support_test=1 set do_test= set msvc=1 +set config= :parse_param @@ -35,14 +36,17 @@ if %1 == --help ( ) if %1 == Debug ( set cmake_opts=%cmake_opts% -DCMAKE_BUILD_TYPE=%1 + set config=--config %1 goto next_param ) if %1 == RelWithDebInfo ( set cmake_opts=%cmake_opts% -DCMAKE_BUILD_TYPE=%1 + set config=--config %1 goto next_param ) if %1 == Release ( set cmake_opts=%cmake_opts% -DCMAKE_BUILD_TYPE=%1 + set config=--config %1 goto next_param ) if %1 == msvc ( @@ -118,6 +122,22 @@ if %1 == nozmq ( set cmake_opts=%cmake_opts% -DLIBSTORED_HAVE_LIBZMQ=OFF goto next_param ) +if %1 == heatshrink ( + set cmake_opts=%cmake_opts% -DLIBSTORED_HAVE_HEATSHRINK=ON + goto next_param +) +if %1 == noheatshrink ( + set cmake_opts=%cmake_opts% -DLIBSTORED_HAVE_HEATSHRINK=OFF + goto next_param +) +if %1 == aes ( + set cmake_opts=%cmake_opts% -DLIBSTORED_HAVE_AES=ON + goto next_param +) +if %1 == noaes ( + set cmake_opts=%cmake_opts% -DLIBSTORED_HAVE_AES=OFF + goto next_param +) if %1 == zth ( set cmake_opts=%cmake_opts% -DLIBSTORED_HAVE_ZTH=ON goto next_param @@ -185,18 +205,18 @@ if errorlevel 1 goto error if not "%do_build%" == "1" goto done -cmake --build . %par% +cmake --build . %config% %par% if errorlevel 1 goto error -cmake --build . --target install %par% +cmake --build . %config% --target install %par% if errorlevel 1 goto error if not "%do_test%" == "1" goto done set CTEST_OUTPUT_ON_FAILURE=1 if %msvc% == 1 ( - cmake --build . --target RUN_TESTS + cmake --build . %config% --target RUN_TESTS if errorlevel 1 goto error ) else ( - cmake --build . --target test + cmake --build . %config% --target test if errorlevel 1 goto error ) @@ -209,18 +229,22 @@ exit /b 0 echo Usage: %0 [^...] [--] [^] echo. echo where opt is: -echo Debug RelWithDebInfo Release -echo Set CMAKE_BUILD_TYPE to this value -echo gcc Use gcc instead of default compiler -echo C++98 C++03 C++11 C++14 C++17 C++20 -echo Set the C++ standard -echo conf Configure only, don't build -echo dev Enable development-related options -echo test Enable building and running tests -echo zmq Enable ZeroMQ integration -echo nozmq Disable ZeroMQ integration -echo zth Enable Zth integration -echo clean Do a clean build +echo Debug RelWithDebInfo Release +echo Set CMAKE_BUILD_TYPE to this value +echo gcc Use gcc instead of default compiler +echo C++98 C++03 C++11 C++14 C++17 C++20 +echo Set the C++ standard +echo conf Configure only, don't build +echo dev Enable development-related options +echo test Enable building and running tests +echo zmq Enable ZeroMQ integration +echo nozmq Disable ZeroMQ integration +echo heatshrink Enable Heatshrink integration +echo noheatshrink Disable Heatshrink integration +echo aes Enable TinyAES integration +echo noaes Disable TinyAES integration +echo zth Enable Zth integration +echo clean Do a clean build popd exit /b 2 goto silent_error diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index d9ec1b9d..da8d4223 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -4,6 +4,12 @@ if(MSVC) add_compile_options(/Wall /WX) + + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + add_compile_options(/MTd) + else() + add_compile_options(/MT) + endif() else() add_compile_options( -Wall diff --git a/examples/int_submodule/1_hello_submodule/CMakeLists.txt b/examples/int_submodule/1_hello_submodule/CMakeLists.txt index cd5f5846..d1863a51 100644 --- a/examples/int_submodule/1_hello_submodule/CMakeLists.txt +++ b/examples/int_submodule/1_hello_submodule/CMakeLists.txt @@ -15,8 +15,8 @@ list(APPEND CMAKE_MODULE_PATH ${LIBSTORED_CLONE}/cmake) # libstored requires python. Make sure that PYTHON_EXECUTABLE is set if you have a venv, otherwise # it is searched for using find_program(). -# If you want to have ZMQ, heatshrink, and Zth you have to do a find_package() yourself, and set -# LIBSTORED_HAVE_LIBZMQ (and friends) to ON. Refer to the list of options() at the top of of +# If you want to have ZMQ, heatshrink, TinyAES, and Zth you have to do a find_package() yourself, +# and set LIBSTORED_HAVE_LIBZMQ (and friends) to ON. Refer to the list of options() at the top of of # cmake/libstored.cmake for details. # Include all libstored functions. diff --git a/examples/lib/CMakeLists.txt b/examples/lib/CMakeLists.txt index 4760e42b..d76fac45 100644 --- a/examples/lib/CMakeLists.txt +++ b/examples/lib/CMakeLists.txt @@ -4,3 +4,12 @@ add_library(example_lib STATIC include/getopt_mini.h src/getopt_mini.cpp) target_include_directories(example_lib PUBLIC include) + +if(MSVC) + target_compile_options(example_lib PRIVATE /W1) + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_options(example_lib PUBLIC /MTd) + else() + target_compile_options(example_lib PUBLIC /MT) + endif() +endif() diff --git a/examples/lib/src/getopt_mini.cpp b/examples/lib/src/getopt_mini.cpp index 6f346de6..e4f99281 100644 --- a/examples/lib/src/getopt_mini.cpp +++ b/examples/lib/src/getopt_mini.cpp @@ -61,4 +61,6 @@ int getopt(int argc, char* const* argv, char const* options) return optopt; } -#endif // !POSIX +#else // POSIX +char dummy_char_to_make_getopt_mini_cpp_non_empty; // NOLINT +#endif // POSIX diff --git a/examples/lossy_sync/main.cpp b/examples/lossy_sync/main.cpp index 119039ef..241a81c2 100644 --- a/examples/lossy_sync/main.cpp +++ b/examples/lossy_sync/main.cpp @@ -8,6 +8,8 @@ * channel. */ +#define _CRT_SECURE_NO_WARNINGS 1 + #include "ExampleSync.h" #include @@ -125,7 +127,9 @@ class ExampleSync : public STORE_T(ExampleSync, stored::Synchronizable, stored:: static void print_help(FILE* out, char const* progname) { - fprintf(out, "Usage: %s [-h] [-v] [-p ] {-s |-c } [-b ]\n", + fprintf(out, + "Usage: %s [-h] [-v] [-p ] {-s |-c } [-b ] [-e " + "]\n", progname); fprintf(out, "where\n"); fprintf(out, " -h Show this help message.\n"); @@ -136,6 +140,9 @@ static void print_help(FILE* out, char const* progname) stored::DebugZmqLayer::DefaultPort); fprintf(out, " -v Verbose output of sync connections.\n"); fprintf(out, " -b Bit error rate (BER) for lossy channel. Default: 0\n"); + fprintf(out, + " -e Encrypt communication with the %zu-byte AES-256 key, read from the file.\n", + (size_t)stored::Aes256Layer::KeySize); } struct Arguments { @@ -143,6 +150,7 @@ struct Arguments { int debug_port = stored::DebugZmqLayer::DefaultPort; std::string client; std::string server; + std::string key; float ber = 0; }; @@ -154,7 +162,7 @@ static Arguments parse_arguments(int argc, char** argv) int c; // flawfinder: ignore - while((c = getopt(argc, argv, "hs:c:p:vb:")) != -1) { + while((c = getopt(argc, argv, "hs:c:p:vb:e:")) != -1) { switch(c) { case 'p': try { @@ -187,6 +195,27 @@ static Arguments parse_arguments(int argc, char** argv) STORED_throw(std::invalid_argument{e.what()}); } break; + case 'e': { + // flawfinder: ignore + FILE* f = fopen(optarg, "rb"); + + if(!f) { + log("Cannot open key file '%s'; %s\n", optarg, strerror(errno)); + STORED_throw(std::invalid_argument{"Cannot open key file"}); + } + + args.key.resize(stored::Aes256Layer::KeySize); + + if(fread(&args.key[0], stored::Aes256Layer::KeySize, 1, f) != 1) { + log("Cannot read key file '%s'; %s\n", optarg, strerror(errno)); + fclose(f); + STORED_throw(std::invalid_argument{"Cannot read key file"}); + } + + fclose(f); + log("Read AES-256 key from '%s'\n", optarg); + break; + } case 'h': print_help(stdout, argv[0]); STORED_throw(exit_now()); @@ -223,7 +252,8 @@ class disconnected : public std::exception {}; class DebugStack { STORED_CLASS_NOCOPY(DebugStack) public: - explicit DebugStack(ExampleSync& store, int port, char const* name = nullptr) + explicit DebugStack( + ExampleSync& store, int port, char const* name = nullptr, char const* key = nullptr) : m_debugLayer{nullptr, port} { if((errno = m_debugLayer.lastError())) { @@ -238,7 +268,16 @@ class DebugStack { m_debugger.setIdentification(m_id.c_str()); m_debugger.map(store); - m_debugLayer.wrap(m_debugger); + + if(key) { + // Encrypted debug channel. + m_aes.reset(new stored::Aes256Layer{key}); + m_aes->wrap(m_debugger); + m_debugLayer.wrap(*m_aes); + } else { + m_debugLayer.wrap(m_debugger); + } + logger_callback = [&](char const* msg) { m_debugger.stream('l', msg); }; } @@ -270,6 +309,7 @@ class DebugStack { std::string m_id; stored::Debugger m_debugger; stored::DebugZmqLayer m_debugLayer; + std::unique_ptr m_aes; stored::PollableZmqSocket m_pollable{m_debugLayer.socket(), stored::Pollable::PollIn}; }; @@ -280,7 +320,8 @@ class SyncStack { STORED_CLASS_NOCOPY(SyncStack) public: explicit SyncStack( - ExampleSync& store, char const* endpoint, bool server, bool verbose, float ber = 0) + ExampleSync& store, char const* endpoint, bool server, bool verbose = false, + float ber = 0, char const* key = nullptr) : m_zmqLayer(nullptr, endpoint, server) { if((errno = m_zmqLayer.lastError())) { @@ -306,6 +347,10 @@ class SyncStack { m_ch1->wrap(*ch1_print); mux.get()->map(1, *m_ch1); + if(key) + // Encrypt communication. + wrap(key); + // We don't want to do ARQ on large messages, so we segment them to some // appropriate size. wrap(32U); @@ -499,6 +544,7 @@ class SyncStack { auto h = m_heartbeat++; if(connected()) { + // flawfinder: ignore char buf[32]; snprintf(buf, sizeof(buf), "ping %u", h); m_ch1->encode(buf, strlen(buf), true); @@ -552,10 +598,13 @@ static void run(Arguments const& args, ExampleSync& store, DebugStack& debugStac std::unique_ptr syncStack; if(!args.client.empty()) { - syncStack.reset(new SyncStack{store, args.client.c_str(), false, args.verbose}); + syncStack.reset(new SyncStack{ + store, args.client.c_str(), false, args.verbose, 0, + args.key.empty() ? nullptr : args.key.c_str()}); } else if(!args.server.empty()) { - syncStack.reset( - new SyncStack{store, args.server.c_str(), true, args.verbose, args.ber}); + syncStack.reset(new SyncStack{ + store, args.server.c_str(), true, args.verbose, args.ber, + args.key.empty() ? nullptr : args.key.c_str()}); } stored::Poller poller; @@ -609,7 +658,8 @@ int main(int argc, char** argv) ExampleSync store; DebugStack debugStack{ - store, args.debug_port, args.client.empty() ? "server" : "client"}; + store, args.debug_port, args.client.empty() ? "server" : "client", + args.key.empty() ? nullptr : args.key.c_str()}; while(true) { try { diff --git a/include/libstored/aes.h b/include/libstored/aes.h new file mode 100644 index 00000000..94d813cb --- /dev/null +++ b/include/libstored/aes.h @@ -0,0 +1,199 @@ +#ifndef LIBSTORED_AES_H +#define LIBSTORED_AES_H +// SPDX-FileCopyrightText: 2020-2025 Jochem Rutgers +// +// SPDX-License-Identifier: MPL-2.0 + +#ifdef __cplusplus + +# include + +# ifdef STORED_HAVE_AES +# include + +namespace stored { + +/*! + * \brief A protocol layer that encrypts data using AES-256 in CTR mode. + * + * The pre-shared key should be provided using #setKey() before connecting. Changing the key during + * an active connection implies a reconnection. + * + * The first (part of the) message after connecting is a random IV for decryption. After that, both + * sides send encrypted data. The last byte of the decoded data indicates the number of bytes to be + * stripped off (including the last byte itself). + * + * The unified mode uses the same key stream for all decrypt and encrypt operations. This would work + * fine in case of a REQ/REP (decode/encode) pattern, where the REQ/REP may start with a new IV. + * The non-unified mode uses two different key streams for decode (decrypt) and encode (encrypt). + * The modes are not mixed, without initializing the IV again. + * + * This layer assumes that the data through the stack is properly framed. For example, it runs on + * top of a #stored::ZmqLayer, #stored::TerminalLayer, or #stored::ArqLayer. + * + * The IV is based on a pseudo-random number generator. Make sure to call \c srand() before. + */ +class Aes256BaseLayer : public ProtocolLayer { + STORED_CLASS_NOCOPY(Aes256BaseLayer) +public: + typedef ProtocolLayer base; + + static char const CmdBidirectional = 'B'; + static char const CmdUnified = 'U'; + + enum { KeySize = 32, BlockSize = 16 }; + +protected: + explicit Aes256BaseLayer( + void const* key = nullptr, ProtocolLayer* up = nullptr, + ProtocolLayer* down = nullptr); + +public: + virtual ~Aes256BaseLayer() override is_default + + virtual void decode(void* buffer, size_t len) override; + virtual void encode(void const* buffer, size_t len, bool last = true) override; +# ifndef DOXYGEN + using base::encode; +# endif + + virtual void reset() override; + virtual void connected() override; + virtual void disconnected() override; + int lastError() const noexcept; + + void setKey(void const* key) noexcept; + void unified(bool enable) noexcept; + bool unified() const noexcept; + void fillRandom(uint8_t* buffer, size_t len) noexcept; + +protected: + void sendIV(bool unified, bool last = true) noexcept; + + /*! + * \brief Low-level initialization for #encrypt(). + * \return 0 on success, otherwise an errno + */ + virtual int initEncrypt(uint8_t const* key, uint8_t const* iv) noexcept = 0; + + /*! + * \brief Low-level initialization for #decrypt(). + * \return 0 on success, otherwise an errno + */ + virtual int initDecrypt(uint8_t const* key, uint8_t const* iv) noexcept = 0; + + /*! + * \brief Low-level initialization for #encrypt() and #decrypt(), using the same IV and key + * stream. + * \return 0 on success, otherwise an errno + */ + virtual int initUnified(uint8_t const* key, uint8_t const* iv) noexcept = 0; + + /*! + * \brief Update the IV on the unified encrypt/decrypt key stream. + */ + virtual int updateUnified(uint8_t const* iv) noexcept = 0; + + /*! + * \brief Decrypt data in \p buffer. + * \param buffer the data to decrypt + * \param len the length of \p buffer + * \param unified when \c true, use the same key stream for encrypt and decrypt + * \return 0 on success, otherwise an errno + * + * This function is expected to call #decodeDecrypted() with the decrypted data, without the + * padding bytes. In-place decryption is allowed. + */ + virtual int decrypt(uint8_t* buffer, size_t len, bool unified) noexcept = 0; + + void decodeDecrypted(void* buffer, size_t len); + + /*! + * \brief Encrypt data in \p buffer. + * \param buffer the data to encrypt + * \param len the length of \p buffer, which is always a multiple of #BlockSize + * \param last whether this is the last block of data + * \param unified when \c true, use the same key stream for encrypt and decrypt + * \return 0 on success, otherwise an errno + * + * This function is expected to call #encodeEncrypted() with the encrypted data. In-place + * encryption is not allowed. + */ + virtual int + encrypt(uint8_t const* buffer, size_t len, bool last, bool unified) noexcept = 0; + + void encodeEncrypted(void const* buffer, size_t len, bool last = true); + +private: + uint8_t m_key[KeySize]; + uint8_t m_buffer[BlockSize]; + size_t m_bufferLen; + + enum EncState +# if STORED_cplusplus >= 201103L + : uint8_t +# endif + { + EncStateDisconnected, + EncStateConnected, + EncStateReady, + EncStateEncoding, + }; + EncState m_encState; + + enum DecState +# if STORED_cplusplus >= 201103L + : uint8_t +# endif + { + DecStateDisconnected, + DecStateConnected, + DecStateReady, + }; + DecState m_decState; + + bool m_unified; + + int m_lastError; +# ifdef STORED_OS_POSIX + unsigned int m_seed; +# endif // STORED_OS_POSIX +}; + +/*! + * \brief A protocol layer that encrypts data using AES-256 in CTR mode using tiny-AES-c. + * + * This is a software implementation of AES-256. You may want to provide a hardware-accelerated + * layer, if your platform supports that. + */ +class Aes256Layer : public Aes256BaseLayer { + STORED_CLASS_NOCOPY(Aes256Layer) +public: + typedef Aes256BaseLayer base; + + explicit Aes256Layer( + void const* key = nullptr, ProtocolLayer* up = nullptr, + ProtocolLayer* down = nullptr); + virtual ~Aes256Layer() override; + +protected: + virtual int initEncrypt(uint8_t const* key, uint8_t const* iv) noexcept override; + virtual int initDecrypt(uint8_t const* key, uint8_t const* iv) noexcept override; + virtual int initUnified(uint8_t const* key, uint8_t const* iv) noexcept override; + virtual int updateUnified(uint8_t const* iv) noexcept override; + virtual int decrypt(uint8_t* buffer, size_t len, bool unified) noexcept override; + virtual int + encrypt(uint8_t const* buffer, size_t len, bool last, bool unified) noexcept override; + +private: + union { + void* m_ctx_enc; + void* m_ctx_uni; + }; + void* m_ctx_dec; +}; + +} // namespace stored +# endif // STORED_HAVE_AES +#endif // __cplusplus +#endif // LIBSTORED_AES_H diff --git a/include/libstored/protocol.h b/include/libstored/protocol.h index 354d912d..d54fb121 100644 --- a/include/libstored/protocol.h +++ b/include/libstored/protocol.h @@ -520,10 +520,12 @@ class SegmentationLayer : public ProtocolLayer { size_t mtu() const final; size_t lowerMtu() const; virtual void reset() override; + virtual void connected() override; virtual void disconnected() override; private: size_t m_mtu; + size_t m_lowerMtu; Vector::type m_decode; size_t m_encoded; }; @@ -955,7 +957,11 @@ class Crc32Layer : public ProtocolLayer { public: typedef ProtocolLayer base; - enum STORED_ANONYMOUS { + enum STORED_ANONYMOUS +# if STORED_cplusplus >= 201103L + : uint32_t +# endif + { polynomial = 0x04c11db7, init = 0xffffffff, final_xor = 0xffffffff diff --git a/include/libstored/synchronizer.h b/include/libstored/synchronizer.h index 82310687..4332e311 100644 --- a/include/libstored/synchronizer.h +++ b/include/libstored/synchronizer.h @@ -560,6 +560,7 @@ class SyncConnection : public ProtocolLayer { virtual void reset() override; virtual void disconnected() override; + virtual void connected() override; protected: Id nextId(); diff --git a/include/stored.h b/include/stored.h index 1aeeb4ce..d6cb7376 100644 --- a/include/stored.h +++ b/include/stored.h @@ -1,10 +1,12 @@ #ifndef LIBSTORED_STORED_H #define LIBSTORED_STORED_H -// SPDX-FileCopyrightText: 2020-2023 Jochem Rutgers +// SPDX-FileCopyrightText: 2020-2025 Jochem Rutgers // // SPDX-License-Identifier: MPL-2.0 #include + +#include #include #include #include diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 3c2d4ac7..6de7cd45 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2020-2023 Jochem Rutgers +# SPDX-FileCopyrightText: 2020-2025 Jochem Rutgers # # SPDX-License-Identifier: MPL-2.0 @@ -49,6 +49,7 @@ set(package_data_ cmake/FindLibstored.cmake.tmpl cmake/LibstoredStoresConfig.cmake.in cmake/libstored.cmake + dist/common/FindTinyAES.cmake dist/common/FindHeatshrink.cmake dist/common/FindZeroMQ.cmake dist/common/FindZth.cmake @@ -66,6 +67,7 @@ set(package_data_ fpga/rtl/variable.vhd fpga/tb/tb_pkg.vhd fpga/vivado/vivado.tcl.tmpl + include/libstored/aes.h include/libstored/allocator.h include/libstored/components.h include/libstored/compress.h @@ -88,6 +90,7 @@ set(package_data_ include/stored.h include/stored_config.h include/stored + src/aes.cpp src/compress.cpp src/debugger.cpp src/directory.cpp diff --git a/python/libstored/asyncio/zmq.py b/python/libstored/asyncio/zmq.py index 1dedcf53..e8bf2f76 100644 --- a/python/libstored/asyncio/zmq.py +++ b/python/libstored/asyncio/zmq.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: MPL-2.0 from __future__ import annotations +import logging import aiofiles import asyncio @@ -1308,6 +1309,7 @@ class ZmqClient(Work): def __init__(self, host : str='localhost', port : int=lprot.default_port, multi : bool=False, timeout : float | None=None, context : None | zmq.asyncio.Context=None, t : str | None = None, use_state : str | None=None, + stack : str | lprot.ProtocolLayer | None=None, *args, **kwargs): super().__init__(*args, **kwargs) @@ -1323,6 +1325,18 @@ def __init__(self, host : str='localhost', port : int=lprot.default_port, self._timestamp_to_time = lambda t: float(t) self._use_state = use_state + if isinstance(stack, str): + self._stack = lprot.build_stack(stack) + elif isinstance(stack, lprot.ProtocolLayer): + self._stack = stack + else: + self._stack = lprot.ProtocolLayer() + self._stack_encoded : bytearray | None = None + self._stack_decoded : bytearray | None = None + self._stack.up = self._stack_up + self._stack.down = self._stack_down + + self._reset() # Events @@ -1666,16 +1680,65 @@ async def req(self, msg : bytes | str) -> bytes | str: finally: self._req_task = None + def _stack_clear(self) -> None: + self._stack_encoded = None + self._stack_decoded = None + + def _stack_up(self, data : lprot.ProtocolLayer.Packet) -> None: + if isinstance(data, str): + data = data.encode() + elif isinstance(data, memoryview): + data = data.cast('B') + + if self._stack_decoded is None: + self._stack_decoded = bytearray(data) + else: + self._stack_decoded.extend(data) + + def _stack_down(self, data : lprot.ProtocolLayer.Packet) -> None: + if isinstance(data, str): + data = data.encode() + elif isinstance(data, memoryview): + data = data.cast('B') + + if self._stack_encoded is None: + self._stack_encoded = bytearray(data) + else: + self._stack_encoded.extend(data) + async def _req(self, msg : bytes) -> bytes: if not self.is_connected(): raise lexc.InvalidState('Not connected') assert self._socket is not None - self.logger.debug('req %s', msg) - await self._socket.send(msg) + + self._stack_clear() + await self._stack.encode(msg) + if self._stack_encoded is None: + raise lexc.OperationFailed('Stack did not produce data') + + if self.logger.getEffectiveLevel() <= logging.DEBUG: + if self._stack_encoded != msg: + self.logger.debug('req %s -> %s', msg, bytes(self._stack_encoded)) + else: + self.logger.debug('req %s', msg) + + await self._socket.send(self._stack_encoded) + rep = b''.join(await self._socket.recv_multipart()) - self.logger.debug('rep %s', rep) - return rep + await self._stack.decode(rep) + if rep and self._stack_decoded is None: + raise lexc.InvalidResponse('Stack did not decode data') + + decoded = bytes(self._stack_decoded) if self._stack_decoded is not None else b'' + + if self.logger.getEffectiveLevel() <= logging.DEBUG: + if self._stack_decoded != rep: + self.logger.debug('rep %s <- %s', decoded, rep) + else: + self.logger.debug('rep %s', decoded) + + return decoded diff --git a/python/libstored/cli/__main__.py b/python/libstored/cli/__main__.py index d9d7740a..7a9f5201 100644 --- a/python/libstored/cli/__main__.py +++ b/python/libstored/cli/__main__.py @@ -16,7 +16,11 @@ @run_sync async def async_main(args : argparse.Namespace): - async with ZmqClient(args.server, args.port, multi=True) as client: + stack = None + if args.encrypted: + stack = lprot.Aes256Layer(args.encrypted, reqrep=True) + + async with ZmqClient(args.server, args.port, multi=True, stack=stack) as client: prefix = '> ' await aiofiles.stdout.write(prefix) await aiofiles.stdout.flush() @@ -29,10 +33,12 @@ async def async_main(args : argparse.Namespace): def main(): parser = argparse.ArgumentParser(description='ZMQ command line client', prog=__package__) - parser.add_argument('-V', action='version', version=__version__) - parser.add_argument('-s', dest='server', type=str, default='localhost', help='ZMQ server to connect to') - parser.add_argument('-p', dest='port', type=int, default=lprot.default_port, help='port') - parser.add_argument('-v', dest='verbose', default=0, help='Enable verbose output', action='count') + parser.add_argument('-V', '--version', action='version', version=__version__) + parser.add_argument('-s', '--server', dest='server', type=str, default='localhost', help='ZMQ server to connect to') + parser.add_argument('-p', '--port', dest='port', type=int, default=lprot.default_port, help='port') + parser.add_argument('-v', '--verbose', dest='verbose', default=0, help='Enable verbose output', action='count') + parser.add_argument('-e', '--encrypt', dest='encrypted', type=str, default=None, + help='Enable AES-256 CTR encryption with the given pre-shared key file', metavar='file') args = parser.parse_args() diff --git a/python/libstored/cmake/__main__.py b/python/libstored/cmake/__main__.py index cb047a15..6cf0ada9 100644 --- a/python/libstored/cmake/__main__.py +++ b/python/libstored/cmake/__main__.py @@ -52,9 +52,9 @@ def main(): description='Generator for find_package(Libstored) in CMake', formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument('-v', dest='verbose', default=0, help='enable verbose output', action='count') + parser.add_argument('-V', '--version', action='version', version=__version__) + parser.add_argument('-v', '--verbose', dest='verbose', default=0, help='enable verbose output', action='count') parser.add_argument('-D', dest='define', metavar='key[=value]', default=[], nargs=1, action='append', help='CMake defines') - parser.add_argument('-V', action='version', version=__version__) parser.add_argument('filename', default='FindLibstored.cmake', nargs='?', type=str, help='Output filename') args = parser.parse_args() diff --git a/python/libstored/generator/__main__.py b/python/libstored/generator/__main__.py index 39a7eed3..e392ac50 100644 --- a/python/libstored/generator/__main__.py +++ b/python/libstored/generator/__main__.py @@ -209,7 +209,7 @@ def pyliteral(x): return f'int({x})' else: return repr(x) - + def pyinit(o): if o.init is None: return None @@ -249,7 +249,7 @@ def yamlstring(s): """ if s is None: return 'null' - + if isinstance(s, str): # Escape backslashes and quotes inside the string esc = s.replace('\\', '\\\\').replace('"', '\\"') @@ -510,10 +510,10 @@ def generate_cmake(libprefix, model_files, output_dir): def main(): parser = argparse.ArgumentParser(description='Store generator', prog=__package__) - parser.add_argument('-V', action='version', version=__version__) - parser.add_argument('-p', type=str, help='libstored prefix for cmake library target') - parser.add_argument('-b', help='generate for big-endian device (default=little)', action='store_true') - parser.add_argument('-v', dest='verbose', default=0, help='Enable verbose output', action='count') + parser.add_argument('-V', '--version',action='version', version=__version__) + parser.add_argument('-p', '--prefix', type=str, help='libstored prefix for cmake library target') + parser.add_argument('-b', '--big', help='generate for big-endian device (default=little)', action='store_true') + parser.add_argument('-v', '--verbose', dest='verbose', default=0, help='Enable verbose output', action='count') parser.add_argument('store_file', type=str, nargs='+', help='store description to parse') parser.add_argument('output_dir', type=str, help='output directory for generated files') @@ -530,9 +530,9 @@ def main(): logger = logging.getLogger('libstored') for f in args.store_file: - generate_store(f, args.output_dir, not args.b) + generate_store(f, args.output_dir, not args.big) - generate_cmake(args.p, args.store_file, args.output_dir) + generate_cmake(args.prefix, args.store_file, args.output_dir) if __name__ == '__main__': main() diff --git a/python/libstored/gui/__main__.py b/python/libstored/gui/__main__.py index 7a8991f1..e0ff6b17 100644 --- a/python/libstored/gui/__main__.py +++ b/python/libstored/gui/__main__.py @@ -682,7 +682,7 @@ def _on_linux_scroll(self, event): return "break" @staticmethod - def _children(widget : tk.Widget) -> set[tk.Widget]: + def _children(widget : tk.BaseWidget) -> set[tk.BaseWidget]: return set(widget.winfo_children()).union(*(ScrollableFrame._children(w) for w in widget.winfo_children())) def bind_scroll(self): @@ -1212,20 +1212,25 @@ def default_poll(self) -> float: # def main(): - parser = argparse.ArgumentParser(prog=__package__, description='ZMQ GUI client', formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument('-V', action='version', version=__version__) - parser.add_argument('-s', dest='server', type=str, default='localhost', help='ZMQ server to connect to') - parser.add_argument('-p', dest='port', type=int, default=lprot.default_port, help='port') - parser.add_argument('-v', dest='verbose', default=0, help='Enable verbose output', action='count') - parser.add_argument('-m', dest='multi', default=False, + parser = argparse.ArgumentParser(prog=__package__, description='ZMQ GUI client', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('-V', '--version', action='version', version=__version__) + parser.add_argument('-s', '--server', dest='server', type=str, default='localhost', help='ZMQ server to connect to') + parser.add_argument('-p', '--port', dest='port', type=int, default=lprot.default_port, help='port') + parser.add_argument('-v', '--verbose', dest='verbose', default=0, help='Enable verbose output', action='count') + parser.add_argument('-m', '--multi', dest='multi', default=False, help='Enable multi-mode; allow multiple simultaneous connections to the same target, ' + 'but it is less efficient.', action='store_true') - parser.add_argument('-c', dest='clear_state', default=False, help='Clear previously saved state', action='store_true') - parser.add_argument('-D', dest='deadlock', default=0, nargs='?', help='Enable deadlock checks after x seconds', type=float, const=10.0) - parser.add_argument('-f', dest='csv', default=None, nargs='?', + parser.add_argument('-c', '--clearstate', dest='clear_state', default=False, + help='Clear previously saved state', action='store_true') + parser.add_argument('-D', '--deadlock', dest='deadlock', default=0, nargs='?', + help='Enable deadlock checks after x seconds', type=float, const=10.0) + parser.add_argument('-f', '--csv', dest='csv', default=None, nargs='?', help='Log auto-refreshed data to csv file. ' + 'The file is truncated upon startup and when the set of auto-refreshed objects change. ' + 'The file name may include strftime() format codes.', const='log.csv') + parser.add_argument('-e', '--encrypt', dest='encrypted', type=str, default=None, + help='Enable AES-256 CTR encryption with the given pre-shared key file', metavar='file') args = parser.parse_args() @@ -1234,6 +1239,8 @@ def main(): 'datefmt': '%H:%M:%S', } + logger = logging.getLogger(__package__) + if args.verbose == 0: logging_config['level'] = logging.WARNING elif args.verbose == 1: @@ -1243,7 +1250,7 @@ def main(): logging.basicConfig(**logging_config) if args.deadlock > 0: - logging.getLogger().info(f'Enable deadlock checks after {args.deadlock} seconds') + logger.info(f'Enable deadlock checks after {args.deadlock} seconds') lexc.DeadlockChecker.default_timeout_s = args.deadlock csv = None @@ -1251,7 +1258,12 @@ def main(): assert isinstance(args.csv, str) csv = laio_csv.CsvExport(laio_csv.generate_filename(args.csv)) - client = laio_zmq.ZmqClient(host=args.server, port=args.port, multi=args.multi, use_state='gui') + stack = None + if args.encrypted: + stack = lprot.Aes256Layer(args.encrypted, reqrep=True) + logger.info(f'Enable AES-256 encryption with key file {args.encrypted}') + + client = laio_zmq.ZmqClient(host=args.server, port=args.port, multi=args.multi, use_state='gui', stack=stack) GUIClient.run(worker=client.worker, client=client, clear_state=args.clear_state, csv=csv) if __name__ == '__main__': diff --git a/python/libstored/log/__main__.py b/python/libstored/log/__main__.py index 187cbe3a..78fc7ed8 100644 --- a/python/libstored/log/__main__.py +++ b/python/libstored/log/__main__.py @@ -22,7 +22,11 @@ async def async_main(args : argparse.Namespace) -> int: if filename != '-': filename = generate_filename(filename, add_timestamp=args.timestamp, unique=args.unique) - async with ZmqClient(args.server, args.port, multi=args.multi) as client: + stack = None + if args.encrypted: + stack = lprot.Aes256Layer(args.encrypted, reqrep=True) + + async with ZmqClient(args.server, args.port, multi=args.multi, stack=stack) as client: objs = [] for o in args.objects: @@ -78,22 +82,24 @@ def main(): parser = argparse.ArgumentParser(prog=__package__, description='ZMQ command line logging client', formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument('-V', action='version', version=__version__) - parser.add_argument('-s', dest='server', type=str, default='localhost', help='ZMQ server to connect to') - parser.add_argument('-p', dest='port', type=int, default=lprot.default_port, help='port') - parser.add_argument('-v', dest='verbose', default=0, help='Enable verbose output', action='count') - parser.add_argument('-f', dest='csv', default='-', + parser.add_argument('-V', '--version', action='version', version=__version__) + parser.add_argument('-s', '--server', dest='server', type=str, default='localhost', help='ZMQ server to connect to') + parser.add_argument('-p', '--port', dest='port', type=int, default=lprot.default_port, help='port') + parser.add_argument('-v', '--verbose', dest='verbose', default=0, help='Enable verbose output', action='count') + parser.add_argument('-f', '--csv', dest='csv', default='-', help='File to log to. The file name may include strftime() format codes.') - parser.add_argument('-t', dest='timestamp', default=False, help='Append time stamp in csv file name', action='store_true') - parser.add_argument('-u', dest='unique', default=False, + parser.add_argument('-t', '--timestamp', dest='timestamp', default=False, help='Append time stamp in csv file name', action='store_true') + parser.add_argument('-u', '--unique', dest='unique', default=False, help='Make sure that the log filename is unique by appending a suffix', action='store_true') - parser.add_argument('-m', dest='multi', default=False, + parser.add_argument('-m', '--multi', dest='multi', default=False, help='Enable multi-mode; allow multiple simultaneous connections to the same target, ' + 'but it is less efficient.', action='store_true') - parser.add_argument('-i', dest='interval', type=float, default=1, help='Poll interval (s)') - parser.add_argument('-d', dest='duration', type=float, default=None, help='Poll duration (s)') + parser.add_argument('-i', '--interval', dest='interval', type=float, default=1, help='Poll interval (s)') + parser.add_argument('-d', '--duration', dest='duration', type=float, default=None, help='Poll duration (s)') parser.add_argument('objects', metavar='obj', type=str, nargs='*', help='Object to poll') - parser.add_argument('-o', dest='objectfile', type=str, action='append', help='File with list of objects to poll') + parser.add_argument('-o', '--objectfile', dest='objectfile', type=str, action='append', help='File with list of objects to poll') + parser.add_argument('-e', '--encrypt', dest='encrypted', type=str, default=None, + help='Enable AES-256 CTR encryption with the given pre-shared key file', metavar='file') args = parser.parse_args() diff --git a/python/libstored/protocol/protocol.py b/python/libstored/protocol/protocol.py index 7c9270d3..9b62979e 100644 --- a/python/libstored/protocol/protocol.py +++ b/python/libstored/protocol/protocol.py @@ -6,6 +6,9 @@ import asyncio import crcmod +import Crypto.Cipher.AES +import Crypto.Random +import Crypto.Util.Padding import inspect import logging import struct @@ -1105,6 +1108,173 @@ async def timeout(self) -> None: +class Aes256Layer(ProtocolLayer): + ''' + A ProtocolLayer that adds AES-256 encryption/decryption. + + The unified mode allows using the same cipher state for encryption and decryption, which uses a + single key stream for all data. + + A specific mode is the reqrep mode, which is a unified mode, but changes the IV for every + request/reply pair. This is useful for a ZeroMQ REQ/REP pattern, where the server may handle + multiple clients simultaneously, which does not allow having a single cipher state. + ''' + + name = 'aes256' + + def __init__(self, key : bytes | str | None=None, *args, unified : bool=False, reqrep : bool=False, **kwargs): + super().__init__(*args, **kwargs) + self._encrypt = None + self._decrypt = None + self._reqrep : bool = reqrep + self._unified : bool = unified or reqrep + + if key is None: + raise ValueError('Key file or binary string must be provided for Aes256Layer') + + self.set_key(key) + + def set_key(self, key : bytes | str) -> None: + ''' + Change the AES-256 key. + + The argument can be either a 32 byte binary string, or a filename containing the key. + ''' + + if isinstance(key, str): + with open(key, 'rb') as f: + key = f.read() + + if len(key) != 32: + raise ValueError('Key must be 32 bytes for AES-256') + + self._key = key + self._encrypt = None + self._decrypt = None + + @property + def unified(self) -> bool: + ''' + Return if unified mode is enabled. + ''' + return self._unified or self._reqrep + + @unified.setter + def unified(self, enable : bool=True) -> None: + ''' + Set unified mode. + ''' + + self._unified = enable + self._encrypt = None + if enable: + self._decrypt = None + else: + self._reqrep = False + + @property + def reqrep(self) -> bool: + ''' + Return if reqrep mode is enabled. + ''' + return self._reqrep + + @reqrep.setter + def reqrep(self, enable : bool=True) -> None: + ''' + Set reqrep mode. + ''' + + self._reqrep = enable + self._unified = enable + self._encrypt = None + if enable: + self._decrypt = None + + async def encode(self, data : ProtocolLayer.Packet) -> None: + if isinstance(data, str): + data = data.encode() + elif isinstance(data, memoryview): + data = data.cast('B') + + data = Crypto.Util.Padding.pad(data, 16) + + prefix = None + if self._encrypt is None or self.reqrep: + prefix = self._iv(self.unified) + assert self._encrypt is not None + + data = self._encrypt.encrypt(data) + + if prefix is not None: + data = prefix + data + await super().encode(data) + + async def decode(self, data : ProtocolLayer.Packet) -> None: + if isinstance(data, str): + data = data.encode() + elif isinstance(data, memoryview): + data = data.cast('B') + + if len(data) > 16 and len(data) % 16 == 1: + # Received IV for decryption + iv = data[1:17] + cypher = Crypto.Cipher.AES.new(self._key, Crypto.Cipher.AES.MODE_CTR, nonce=b'', initial_value=iv) + if data[0:1] == b'U': + self.logger.debug('Received IV for unified operation') + self._decrypt = cypher + self._encrypt = cypher + self._unified = True + self._reqrep = False + elif data[0:1] == b'B': + self.logger.debug('Received IV for decryption') + self._decrypt = cypher + self._unified = False + self._reqrep = False + else: + self.logger.debug('Invalid IV prefix') + self._decrypt = None + + data = data[17:] + + if self._decrypt is None: + self.logger.debug('Got data before IV, waiting for IV') + self._decrypt = None + return + + if len(data) % 16 != 0: + self.logger.debug('Data length not multiple of 16, dropped') + self._decrypt = None + return + + if self.unified: + # Encryption is the same as decryption for AES, but the API does not allow mixing the calls. + data = self._decrypt.encrypt(data) + else: + data = self._decrypt.decrypt(data) + + try: + data = Crypto.Util.Padding.unpad(data, 16) + except ValueError: + self.logger.debug('Invalid padding, dropped') + self._decrypt = None + return + + await super().decode(data) + + def _iv(self, unified : bool) -> bytes: + iv = Crypto.Random.get_random_bytes(16) + # Make sure not to wrap around the counter soon. + iv = bytes([iv[0] & 0x0f]) + iv[1:] + self._encrypt = Crypto.Cipher.AES.new(self._key, Crypto.Cipher.AES.MODE_CTR, nonce=b'', initial_value=iv) + if unified: + self._decrypt = self._encrypt + return b'U' + iv + else: + return b'B' + iv + + + layer_types : list[typing.Type[ProtocolLayer]] = [ AsciiEscapeLayer, TerminalLayer, @@ -1118,6 +1288,7 @@ async def timeout(self) -> None: LoopbackLayer, RawLayer, MuxLayer, + Aes256Layer, ] def register_layer_type(layer_type : typing.Type[ProtocolLayer]) -> None: diff --git a/python/libstored/wrapper/serial.py b/python/libstored/wrapper/serial.py index 0044ab2b..d9b59dd5 100644 --- a/python/libstored/wrapper/serial.py +++ b/python/libstored/wrapper/serial.py @@ -16,15 +16,15 @@ def main(): parser = argparse.ArgumentParser(description='serial wrapper to ZMQ server', formatter_class=argparse.ArgumentDefaultsHelpFormatter, prog=__package__) - parser.add_argument('-V', action='version', version=__version__) - parser.add_argument('-l', dest='zmqlisten', type=str, default='*', help='ZMQ listen address') - parser.add_argument('-p', dest='zmqport', type=int, default=lprot.default_port, help='ZMQ port') + parser.add_argument('-V', '--version', action='version', version=__version__) + parser.add_argument('-l', '--listen', dest='zmqlisten', type=str, default='*', help='ZMQ listen address') + parser.add_argument('-p', '--port', dest='zmqport', type=int, default=lprot.default_port, help='ZMQ port') parser.add_argument('port', help='serial port') parser.add_argument('baud', nargs='?', type=int, default=115200, help='baud rate') - parser.add_argument('-r', dest='rtscts', default=False, help='RTS/CTS flow control', action='store_true') - parser.add_argument('-x', dest='xonxoff', default=False, help='XON/XOFF flow control', action='store_true') - parser.add_argument('-v', dest='verbose', default=0, help='Enable verbose output', action='count') - parser.add_argument('-S', dest='stack', type=str, default='ascii,pubterm,stdin', help='protocol stack') + parser.add_argument('-r', '--rtscts', dest='rtscts', default=False, help='RTS/CTS flow control', action='store_true') + parser.add_argument('-x', '--xonxoff', dest='xonxoff', default=False, help='XON/XOFF flow control', action='store_true') + parser.add_argument('-v', '--verbose', dest='verbose', default=0, help='Enable verbose output', action='count') + parser.add_argument('-S', '--stack', dest='stack', type=str, default='ascii,pubterm,stdin', help='protocol stack') args = parser.parse_args() diff --git a/python/libstored/wrapper/stdio.py b/python/libstored/wrapper/stdio.py index 545a0b13..b3d92baf 100644 --- a/python/libstored/wrapper/stdio.py +++ b/python/libstored/wrapper/stdio.py @@ -16,11 +16,11 @@ def main(): parser = argparse.ArgumentParser(description='stdin/stdout wrapper to ZMQ server', formatter_class=argparse.ArgumentDefaultsHelpFormatter, prog=__package__) - parser.add_argument('-V', action='version', version=__version__) - parser.add_argument('-l', dest='listen', type=str, default='*', help='listen address') - parser.add_argument('-p', dest='port', type=int, default=lprot.default_port, help='port') - parser.add_argument('-S', dest='stack', type=str, default='ascii,pubterm,stdin', help='protocol stack') - parser.add_argument('-v', dest='verbose', default=0, help='Enable verbose output', action='count') + parser.add_argument('-V', '--version', action='version', version=__version__) + parser.add_argument('-l', '--listen', dest='listen', type=str, default='*', help='listen address') + parser.add_argument('-p', '--port', dest='port', type=int, default=lprot.default_port, help='port') + parser.add_argument('-S', '--stack', dest='stack', type=str, default='ascii,pubterm,stdin', help='protocol stack') + parser.add_argument('-v', '--verbose', dest='verbose', default=0, help='Enable verbose output', action='count') parser.add_argument('command') parser.add_argument('args', nargs='*') diff --git a/python/setup.cfg b/python/setup.cfg index cba12b49..e15085cf 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -32,6 +32,7 @@ install_requires = matplotlib natsort platformdirs + pycryptodome pyserial >= 3.1 pyzmq textx diff --git a/sphinx/Doxyfile.in b/sphinx/Doxyfile.in index ce9b4986..cbd464cc 100644 --- a/sphinx/Doxyfile.in +++ b/sphinx/Doxyfile.in @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2020-2023 Jochem Rutgers +# SPDX-FileCopyrightText: 2020-2025 Jochem Rutgers # # SPDX-License-Identifier: CC0-1.0 @@ -2083,6 +2083,7 @@ PREDEFINED = DOXYGEN \ _DEBUG \ STORED_HAVE_ZMQ \ STORED_HAVE_HEATSHRINK \ + STORED_HAVE_AES \ STORED_DRAFT_API \ zth_fiber= \ UNUSED(x)= \ diff --git a/sphinx/doc/cpp_protocol.rst b/sphinx/doc/cpp_protocol.rst index 983917f7..304468f2 100644 --- a/sphinx/doc/cpp_protocol.rst +++ b/sphinx/doc/cpp_protocol.rst @@ -94,6 +94,7 @@ The inheritance of the layers is shown below. ProtocolLayer <|-- IdleCheckLayer ProtocolLayer <|-- CallbackLayer ProtocolLayer <|-- MuxLayer + ProtocolLayer <|-- Aes256Layer abstract ArqLayer SegmentationLayer -[hidden]--> ArqLayer @@ -134,6 +135,11 @@ The inheritance of the layers is shown below. FifoLoopback --> FifoLoopback1 +stored::Aes256Layer +------------------- + +.. doxygenclass:: stored::Aes256Layer + stored::AsciiEscapeLayer ------------------------ @@ -230,7 +236,7 @@ stored::SegmentationLayer .. doxygenclass:: stored::SegmentationLayer stored::SerialLayer -------------------------- +------------------- .. doxygenclass:: stored::SerialLayer @@ -242,7 +248,7 @@ stored::StdioLayer stored::SyncZmqLayer -------------------- -.. doxygenclass:: stored::SyncZmqLayer +It is a typedef for ``stored::ZmqLayer``. stored::TerminalLayer --------------------- @@ -250,7 +256,7 @@ stored::TerminalLayer .. doxygenclass:: stored::TerminalLayer stored::XsimLayer ------------------------ +----------------- .. doxygenclass:: stored::XsimLayer @@ -260,7 +266,7 @@ stored::ZmqBaseLayer .. doxygenclass:: stored::ZmqBaseLayer stored::ZmqLayer --------------------- +---------------- .. doxygenclass:: stored::ZmqLayer diff --git a/sphinx/doc/py_py.rst b/sphinx/doc/py_py.rst index 0cac45fb..c4d0a979 100644 --- a/sphinx/doc/py_py.rst +++ b/sphinx/doc/py_py.rst @@ -22,6 +22,8 @@ Protocol layers :members: :undoc-members: +.. autoclass:: libstored.protocol.Aes256Layer + .. autoclass:: libstored.protocol.AsciiEscapeLayer .. autoclass:: libstored.protocol.TerminalLayer diff --git a/src/aes.cpp b/src/aes.cpp new file mode 100644 index 00000000..a6c738ff --- /dev/null +++ b/src/aes.cpp @@ -0,0 +1,445 @@ +// SPDX-FileCopyrightText: 2020-2025 Jochem Rutgers +// +// SPDX-License-Identifier: MPL-2.0 + +#include + +#ifdef STORED_HAVE_AES + +# include + +extern "C" { +# include +} // extern "C" + +# ifdef STORED_OS_POSIX +# include +# endif // STORED_OS_POSIX + + +namespace stored { + + + +//////////////////////////////////////////////////////////////// +// Aes256BaseLayer +// + +Aes256BaseLayer::Aes256BaseLayer(void const* key, ProtocolLayer* up, ProtocolLayer* down) + : base(up, down) + , m_key() + , m_buffer() + , m_bufferLen() + , m_encState(EncStateDisconnected) + , m_decState(DecStateDisconnected) + , m_unified() + , m_lastError(ENOTCONN) +# ifdef STORED_OS_POSIX + // NOLINTNEXTLINE + , m_seed((unsigned int)(uintptr_t)this ^ (unsigned int)time(nullptr)) +# endif // STORED_OS_POSIX +{ + setKey(key); +} + +void Aes256BaseLayer::reset() +{ + m_encState = EncStateDisconnected; + m_decState = DecStateDisconnected; + m_lastError = ENOTCONN; + base::reset(); +} + +void Aes256BaseLayer::connected() +{ + size_t mtu = this->mtu(); + if(mtu && mtu < BlockSize + 1) { + // MTU too small. + m_lastError = EMSGSIZE; + if(m_encState != EncStateDisconnected || m_decState != DecStateDisconnected) + disconnected(); + return; + } + + m_encState = EncStateConnected; + if(m_decState == DecStateDisconnected) + m_decState = DecStateConnected; + m_lastError = 0; + base::connected(); +} + +void Aes256BaseLayer::disconnected() +{ + m_encState = EncStateDisconnected; + m_decState = DecStateDisconnected; + if(!m_lastError) + m_lastError = ENOTCONN; + base::disconnected(); +} + +int Aes256BaseLayer::lastError() const noexcept +{ + return m_lastError; +} + +void Aes256BaseLayer::decode(void* buffer, size_t len) +{ + uint8_t* buffer_ = static_cast(buffer); + +again: + switch(m_decState) { + case DecStateDisconnected: + // Ignore data. + return; + case DecStateConnected: + case DecStateReady: { + // Decrypt data. + if(len == 0) { + // Nothing to do. + return; + } + if(len > BlockSize && len % BlockSize == 1) { + // Got IV for decryption. + switch(buffer_[0]) { + case CmdBidirectional: + if(unified()) + unified(false); + m_lastError = initDecrypt(m_key, buffer_ + 1); + break; + case CmdUnified: + m_unified = true; + if(m_encState == EncStateConnected) + m_encState = EncStateReady; + m_lastError = initUnified(m_key, buffer_ + 1); + break; + default: + // Invalid command. + m_lastError = EINVAL; + } + + if(m_lastError) { + // Initialization error. + disconnected(); + return; + } + + m_decState = DecStateReady; + buffer_ += BlockSize + 1; + len -= BlockSize + 1; + goto again; + } + if(m_decState != DecStateReady || len % BlockSize != 0) { + // Invalid block. + m_lastError = EINVAL; + disconnected(); + return; + } + + m_lastError = decrypt(buffer_, len, unified()); + if(m_lastError) { + // Decryption error. + disconnected(); + return; + } + + // decrypt() should have called base::decode(). + break; + } + default:; + } +} + +/*! + * \brief Pass decrypted data upstream. + */ +void Aes256BaseLayer::decodeDecrypted(void* buffer, size_t len) +{ + base::decode(buffer, len); +} + +/*! + * \brief Pass encrypted data downstream. + */ +void Aes256BaseLayer::encodeEncrypted(void const* buffer, size_t len, bool last) +{ + base::encode(buffer, len, last); +} + +void Aes256BaseLayer::encode(void const* buffer, size_t len, bool last) +{ +again: + switch(m_encState) { + case EncStateDisconnected: + // Ignore data. + return; + case EncStateConnected: + m_encState = EncStateReady; + if(unified()) { + if(m_decState == DecStateConnected) + m_decState = DecStateReady; + sendIV(true, false); + } else { + sendIV(false, false); + } + goto again; + case EncStateReady: { + // Encrypt data. + if(len == 0) { + // Nothing to do. + return; + } + + m_encState = EncStateEncoding; + m_bufferLen = 0; + STORED_FALLTHROUGH + } + case EncStateEncoding: { + uint8_t const* buffer_ = static_cast(buffer); + int res = 0; + while(len && !res) { + if(likely(m_bufferLen == 0)) { + size_t chunk = len & ~((size_t)BlockSize - 1U); + if(likely(chunk)) { + // Full chunks to encrypt directly. + res = encrypt(buffer_, chunk, false, unified()); + len -= chunk; + buffer_ += chunk; + continue; + } + } + + // We have partial data in the buffer. + // Copy first. + size_t copy = BlockSize - m_bufferLen; + if(copy > len) + copy = len; + memcpy(m_buffer + m_bufferLen, buffer_, copy); + m_bufferLen += copy; + len -= copy; + buffer_ += copy; + if(m_bufferLen == BlockSize) { + // Encrypt full buffer. + res = encrypt(m_buffer, BlockSize, false, unified()); + m_bufferLen = 0; + continue; + } + } + + if(!res && last) { + // Finalize. + stored_assert(m_bufferLen < BlockSize); + + // Add PKCS#7 padding. + size_t padding = BlockSize - m_bufferLen % BlockSize; + for(size_t i = m_bufferLen; i < BlockSize; ++i) + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index) + m_buffer[i] = static_cast(padding); + + res = encrypt(m_buffer, BlockSize, true, unified()); + m_encState = EncStateReady; + } + + if(res) { + // Encryption error. + m_lastError = res; + + if(last) + base::encode(nullptr, 0, true); + + disconnected(); + } + break; + } + default:; + } +} + +/*! + * \brief Send out the initialization vector for encryption (so for decryption by the peer). + */ +void Aes256BaseLayer::sendIV(bool unified, bool last) noexcept +{ + uint8_t buf[BlockSize + 1]; + buf[0] = (uint8_t)(unified ? CmdUnified : CmdBidirectional); + fillRandom(buf + 1, BlockSize); + // Make sure not to wrap around the counter soon. + buf[1] = (uint8_t)(buf[1] & 0x0fU); + + m_lastError = unified ? initUnified(m_key, buf + 1) : initEncrypt(m_key, buf + 1); + if(m_lastError) { + // Initialization error. + disconnected(); + return; + } + + base::encode(buf, sizeof(buf), last); +} + +/*! + * \brief Set the pre-shared key. + * + * Make sure to switch the key at the same time on both sides. + */ +void Aes256BaseLayer::setKey(void const* key) noexcept +{ + if(!key) + memset(m_key, 0, KeySize); + else + memcpy(m_key, key, KeySize); + + if(m_encState != EncStateDisconnected) + m_encState = EncStateConnected; + if(unified() && m_decState != DecStateDisconnected) + m_decState = DecStateConnected; +} + +/*! + * \brief Configure unified mode. + */ +void Aes256BaseLayer::unified(bool enable) noexcept +{ + m_unified = enable; + + stored_assert(m_encState != EncStateEncoding); + + if(m_encState != EncStateDisconnected) + m_encState = EncStateConnected; + if(m_decState != DecStateDisconnected) + m_decState = DecStateConnected; +} + +bool Aes256BaseLayer::unified() const noexcept +{ + return m_unified; +} + +/*! + * \brief Fill \p buffer with \p len pseudo-random bytes. + */ +void Aes256BaseLayer::fillRandom(uint8_t* buffer, size_t len) noexcept +{ + stored_assert(len == 0 || buffer); + + for(size_t i = 0; i < len; ++i) { + buffer[i] = (uint8_t) +# ifdef STORED_OS_POSIX + rand_r(&m_seed); +# else // !STORED_OS_POSIX + rand(); +# endif // !STORED_OS_POSIX + } +} + + + +//////////////////////////////////////////////////////////////// +// Aes256Layer using tiny-AES-c +// + +# if !AES256 +# error "AES256 not defined in aes.h" +# endif + +# if !CTR +# error "CTR not defined in aes.h" +# endif + +// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init) +Aes256Layer::Aes256Layer(void const* key, ProtocolLayer* up, ProtocolLayer* down) + : base(key, up, down) + , m_ctx_enc() + , m_ctx_dec() +{ + static_assert(Aes256Layer::KeySize == AES_KEYLEN, ""); + static_assert(Aes256Layer::BlockSize == AES_BLOCKLEN, ""); + + // NOLINTNEXTLINE + m_ctx_enc = new struct AES_ctx; + + try { + // NOLINTNEXTLINE + m_ctx_dec = new struct AES_ctx; + } catch(...) { + delete static_cast(m_ctx_enc); + m_ctx_enc = nullptr; + STORED_rethrow; + } +} + +Aes256Layer::~Aes256Layer() +{ + delete static_cast(m_ctx_enc); + delete static_cast(m_ctx_dec); +} + +int Aes256Layer::initEncrypt(uint8_t const* key, uint8_t const* iv) noexcept +{ + struct AES_ctx* ctx = static_cast(m_ctx_enc); + AES_init_ctx_iv(ctx, key, iv); + return 0; +} + +int Aes256Layer::initDecrypt(uint8_t const* key, uint8_t const* iv) noexcept +{ + struct AES_ctx* ctx = static_cast(m_ctx_dec); + AES_init_ctx_iv(ctx, key, iv); + return 0; +} + +int Aes256Layer::initUnified(uint8_t const* key, uint8_t const* iv) noexcept +{ + struct AES_ctx* ctx = static_cast(m_ctx_uni); + AES_init_ctx_iv(ctx, key, iv); + return 0; +} + +int Aes256Layer::updateUnified(uint8_t const* iv) noexcept +{ + struct AES_ctx* ctx = static_cast(m_ctx_uni); + AES_ctx_set_iv(ctx, iv); + return 0; +} + +int Aes256Layer::decrypt(uint8_t* buffer, size_t len, bool unified) noexcept +{ + if(!len) + return 0; + + stored_assert(len % BlockSize == 0); + stored_assert(buffer); + AES_CTR_xcrypt_buffer( + static_cast(unified ? m_ctx_uni : m_ctx_dec), buffer, len); + + size_t padding = buffer[len - 1]; + if(padding == 0 || padding > BlockSize) + // Invalid padding. + return EINVAL; + + if(padding >= len) + return 0; + + decodeDecrypted(buffer, len - padding); + return 0; +} + +int Aes256Layer::encrypt(uint8_t const* buffer, size_t len, bool last, bool unified) noexcept +{ + stored_assert(!len || buffer); + stored_assert(len % BlockSize == 0); + + uint8_t buf[BlockSize]; + for(; len; len -= BlockSize, buffer += BlockSize) { + memcpy(buf, buffer, BlockSize); + AES_CTR_xcrypt_buffer( + static_cast(unified ? m_ctx_uni : m_ctx_enc), buf, + BlockSize); + encodeEncrypted(buf, BlockSize, last && len == BlockSize); + } + + return 0; +} + +} // namespace stored +#else // !STORED_HAVE_AES +char dummy_char_to_make_aes_cpp_non_empty; // NOLINT +#endif // STORED_HAVE_AES diff --git a/src/protocol.cpp b/src/protocol.cpp index cea4c143..4cba5809 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -358,8 +358,11 @@ size_t TerminalLayer::mtu() const SegmentationLayer::SegmentationLayer(size_t mtu, ProtocolLayer* up, ProtocolLayer* down) : base(up, down) , m_mtu(mtu) + , m_lowerMtu() , m_encoded() -{} +{ + lowerMtu(); +} void SegmentationLayer::reset() { @@ -368,6 +371,18 @@ void SegmentationLayer::reset() base::reset(); } +void SegmentationLayer::connected() +{ + m_lowerMtu = lowerMtu(); + if(m_lowerMtu == 0) + m_lowerMtu = std::numeric_limits::max(); + else if(m_lowerMtu == 1) + m_lowerMtu = 2; + + m_encoded = 0; + base::connected(); +} + void SegmentationLayer::disconnected() { m_decode.clear(); @@ -423,14 +438,10 @@ void SegmentationLayer::encode(void const* buffer, size_t len, bool last) { char const* buffer_ = static_cast(buffer); - size_t mtu = lowerMtu(); - if(mtu == 0) - mtu = std::numeric_limits::max(); - else if(mtu == 1) - mtu = 2; + stored_assert(m_lowerMtu > m_encoded); while(len) { - size_t remaining = mtu - m_encoded - 1; + size_t remaining = m_lowerMtu - m_encoded - 1; size_t chunk = std::min(len, remaining); if(chunk) { @@ -2356,7 +2367,9 @@ void impl::Loopback1::encode(void const* buffer, size_t len, bool last) Loopback::Loopback(ProtocolLayer& a, ProtocolLayer& b) : m_a2b(a, b) , m_b2a(b, a) -{} +{ + a.connected(); +} /*! * \brief Reserve heap memory to assemble partial messages. diff --git a/src/synchronizer.cpp b/src/synchronizer.cpp index 3dd9c4c0..d4ea77cd 100644 --- a/src/synchronizer.cpp +++ b/src/synchronizer.cpp @@ -848,7 +848,12 @@ void SyncConnection::disconnected() { dropNonSources(); base::disconnected(); +} + +void SyncConnection::connected() +{ helloAgain(); + base::connected(); } /*! diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 88e431e5..2be6a444 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -15,12 +15,6 @@ set(LIBSTORED_DRAFT_API "ON") if(MSVC) add_compile_options(/W1 /WX) - - if(CMAKE_BUILD_TYPE STREQUAL "Debug") - add_compile_options(/MTd) - else() - add_compile_options(/MT) - endif() else() add_compile_options( -Wall diff --git a/tests/test_debugger.cpp b/tests/test_debugger.cpp index 85231864..454a8e1a 100644 --- a/tests/test_debugger.cpp +++ b/tests/test_debugger.cpp @@ -24,7 +24,7 @@ TEST(Debugger, Capabilities) ll.wrap(d); DECODE(d, "?"); - EXPECT_GT(ll.encoded().at(0).size(), 1); + EXPECT_GT(ll.encoded().at(0).size(), 1U); } TEST(Debugger, Identification) @@ -97,7 +97,7 @@ TEST(Debugger, List) d.list([&](char const* name, stored::DebugVariant&) { names.push_back(name); }); // We should find something. - EXPECT_GT(names.size(), 10); + EXPECT_GT(names.size(), 10U); // Check a few names. EXPECT_TRUE(std::find(names.begin(), names.end(), "/default int8") != names.end()); @@ -152,7 +152,7 @@ TEST(Debugger, ListMulti) d.list([&](char const* name, stored::DebugVariant&) { names.push_back(name); }); // We should find something. - EXPECT_GT(names.size(), 10); + EXPECT_GT(names.size(), 10U); // Check a few names. EXPECT_TRUE(std::find(names.begin(), names.end(), "/first/default int8") != names.end()); @@ -307,15 +307,15 @@ TEST(Debugger, WriteMem) LoggingLayer ll; ll.wrap(d); - uint32_t i = 0x12345678; + uint32_t i = 0x12345678U; char buf[32]; snprintf(buf, sizeof(buf), "W%" PRIxPTR " abcdef01", (uintptr_t)&i); d.decode(buf, strlen(buf)); EXPECT_EQ(ll.encoded().at(0), "!"); #ifdef STORED_LITTLE_ENDIAN - EXPECT_EQ(i, 0x01efcdab); + EXPECT_EQ(i, 0x01efcdabU); #else - EXPECT_EQ(i, 0xabcdef01); + EXPECT_EQ(i, 0xabcdef01U); #endif } diff --git a/tests/test_directory.cpp b/tests/test_directory.cpp index 2780273a..a2ad19f2 100644 --- a/tests/test_directory.cpp +++ b/tests/test_directory.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2020-2023 Jochem Rutgers +// SPDX-FileCopyrightText: 2020-2025 Jochem Rutgers // // SPDX-License-Identifier: MPL-2.0 @@ -113,7 +113,7 @@ TEST(Directory, List) }); // We should find something. - EXPECT_GT(names.size(), 10); + EXPECT_GT(names.size(), 10U); // Check a few names. EXPECT_TRUE(std::find(names.begin(), names.end(), "/default int8") != names.end()); @@ -131,12 +131,12 @@ TEST(Directory, List) TEST(Directory, Constexpr) { #ifdef STORED_COMPILER_GCC -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Waddress" +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Waddress" #endif static_assert(stored::TestStoreData::shortDirectory() != nullptr, ""); #ifdef STORED_COMPILER_GCC -# pragma GCC diagnostic pop +# pragma GCC diagnostic pop #endif constexpr auto v = diff --git a/tests/test_fifo.cpp b/tests/test_fifo.cpp index 2663b835..1b872dd2 100644 --- a/tests/test_fifo.cpp +++ b/tests/test_fifo.cpp @@ -48,27 +48,27 @@ TEST(Fifo, UnboundedFifo) f.push_back(1); EXPECT_FALSE(f.empty()); EXPECT_FALSE(f.full()); - EXPECT_EQ(f.size(), 1u); - EXPECT_EQ(f.front(), 1u); + EXPECT_EQ(f.size(), 1U); + EXPECT_EQ(f.front(), 1); EXPECT_EQ_VIEW(f); f.push_back(2); - EXPECT_EQ(f.size(), 2u); - EXPECT_EQ(f.front(), 1u); + EXPECT_EQ(f.size(), 2U); + EXPECT_EQ(f.front(), 1); EXPECT_EQ_VIEW(f); f.push_back(3); f.push_back(4); - EXPECT_EQ(f.size(), 4u); + EXPECT_EQ(f.size(), 4U); EXPECT_EQ_VIEW(f); f.pop_front(); - EXPECT_EQ(f.front(), 2u); + EXPECT_EQ(f.front(), 2); EXPECT_EQ_VIEW(f); f.push_back(5); // Not empty, should still be growing. - EXPECT_EQ(f.size(), 5u); + EXPECT_EQ(f.size(), 5U); EXPECT_EQ_VIEW(f); f.pop_front(); @@ -82,8 +82,8 @@ TEST(Fifo, UnboundedFifo) f.push_back(7); f.push_back(8); // Restarted at beginning of buffer. - EXPECT_EQ(f.size(), 5u); - EXPECT_EQ(f.front(), 6u); + EXPECT_EQ(f.size(), 5U); + EXPECT_EQ(f.front(), 6); EXPECT_EQ_VIEW(f); } @@ -94,45 +94,45 @@ TEST(Fifo, BoundedFifo) EXPECT_TRUE(f.bounded()); EXPECT_TRUE(f.empty()); EXPECT_FALSE(f.full()); - EXPECT_LE(f.size(), 5u); + EXPECT_LE(f.size(), 5U); EXPECT_EQ_VIEW(f); f.push_back(1); EXPECT_FALSE(f.empty()); EXPECT_FALSE(f.full()); - EXPECT_LE(f.size(), 5u); - EXPECT_EQ(f.front(), 1u); + EXPECT_LE(f.size(), 5U); + EXPECT_EQ(f.front(), 1); EXPECT_EQ_VIEW(f); f.push_back(2); - EXPECT_LE(f.size(), 5u); - EXPECT_EQ(f.front(), 1u); + EXPECT_LE(f.size(), 5U); + EXPECT_EQ(f.front(), 1); EXPECT_EQ_VIEW(f); f.push_back(3); f.push_back(4); EXPECT_TRUE(f.full()); - EXPECT_LE(f.size(), 5u); + EXPECT_LE(f.size(), 5U); EXPECT_EQ_VIEW(f); f.pop_front(); EXPECT_FALSE(f.full()); - EXPECT_EQ(f.front(), 2u); + EXPECT_EQ(f.front(), 2); EXPECT_EQ_VIEW(f); f.push_back(5); EXPECT_TRUE(f.full()); - EXPECT_LE(f.size(), 5u); + EXPECT_LE(f.size(), 5U); EXPECT_EQ_VIEW(f); f.pop_front(); - EXPECT_EQ(f.front(), 3u); + EXPECT_EQ(f.front(), 3); EXPECT_EQ_VIEW(f); f.pop_front(); - EXPECT_EQ(f.front(), 4u); + EXPECT_EQ(f.front(), 4); EXPECT_EQ_VIEW(f); f.pop_front(); - EXPECT_EQ(f.front(), 5u); + EXPECT_EQ(f.front(), 5); EXPECT_EQ_VIEW(f); f.pop_front(); EXPECT_TRUE(f.empty()); @@ -158,9 +158,9 @@ TEST(Fifo, IterateFifo) } // Should stop at the content when the iterator was created. - EXPECT_EQ(f.front(), 11u); + EXPECT_EQ(f.front(), 11); f.pop_front(); - EXPECT_EQ(f.front(), 12u); + EXPECT_EQ(f.front(), 12); f.pop_front(); EXPECT_TRUE(f.empty()); } @@ -181,14 +181,14 @@ TEST(Fifo, UnboundedMessageFifo) EXPECT_TRUE(f.empty()); EXPECT_TRUE(f.push_back("abc", 3)); EXPECT_FALSE(f.empty()); - EXPECT_EQ(f.available(), 1u); - EXPECT_EQ(f.size(), 3u); + EXPECT_EQ(f.available(), 1U); + EXPECT_EQ(f.size(), 3U); EXPECT_EQ_MSG(f.front(), "abc"); EXPECT_TRUE(f.push_back("defg", 4)); - EXPECT_EQ(f.available(), 2u); - EXPECT_EQ(f.size(), 7u); + EXPECT_EQ(f.available(), 2U); + EXPECT_EQ(f.size(), 7U); EXPECT_EQ_MSG(f.front(), "abc"); f.pop_front(); EXPECT_EQ_MSG(f.front(), "defg"); @@ -199,7 +199,7 @@ TEST(Fifo, UnboundedMessageFifo) EXPECT_TRUE(f.push_back(stored::MessageView{"hi", 2})); EXPECT_EQ_MSG(f.front(), "hi"); EXPECT_FALSE(f.empty()); - EXPECT_EQ(f.size(), 7u); + EXPECT_EQ(f.size(), 7U); EXPECT_TRUE(f.append_back("jk", 2)); EXPECT_EQ_MSG(f.front(), "hi"); @@ -218,16 +218,16 @@ TEST(Fifo, BoundedMessageFifo) stored::MessageFifo<16, 4> f; EXPECT_TRUE(f.bounded()); - EXPECT_EQ(f.space(), 15u); + EXPECT_EQ(f.space(), 15U); EXPECT_FALSE(f.full()); - EXPECT_EQ(f.push_back({{"abc", 3}, {"defg", 4}, {"ghijk", 5}, {"lmn", 3}}), 4u); - EXPECT_EQ(f.space(), 0u); + EXPECT_EQ(f.push_back({{"abc", 3}, {"defg", 4}, {"ghijk", 5}, {"lmn", 3}}), 4U); + EXPECT_EQ(f.space(), 0U); // Does not fit EXPECT_FALSE(f.push_back({"h", 1})); EXPECT_EQ_MSG(f.front(), "abc"); f.pop_front(); - EXPECT_EQ(f.space(), 2u); + EXPECT_EQ(f.space(), 2U); // Too long message. EXPECT_FALSE(f.push_back({"hijl", 4})); diff --git a/tests/test_function.cpp b/tests/test_function.cpp index 870d5e8e..da6e0e08 100644 --- a/tests/test_function.cpp +++ b/tests/test_function.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2020-2023 Jochem Rutgers +// SPDX-FileCopyrightText: 2020-2025 Jochem Rutgers // // SPDX-License-Identifier: MPL-2.0 @@ -96,8 +96,8 @@ TEST(Function, WriteOnly) { FunctionTestStore store; char buffer[] = "hi all!"; - EXPECT_EQ(store.f_write_only.get(buffer, sizeof(buffer)), 0); - EXPECT_EQ(store.f_write_only.set(buffer, strlen(buffer)), 4u); + EXPECT_EQ(store.f_write_only.get(buffer, sizeof(buffer)), 0U); + EXPECT_EQ(store.f_write_only.set(buffer, strlen(buffer)), 4U); } TEST(Function, FreeFunction) diff --git a/tests/test_pipes.cpp b/tests/test_pipes.cpp index 0a38ff3e..fa036481 100644 --- a/tests/test_pipes.cpp +++ b/tests/test_pipes.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2020-2023 Jochem Rutgers +// SPDX-FileCopyrightText: 2020-2025 Jochem Rutgers // // SPDX-License-Identifier: MPL-2.0 @@ -656,8 +656,8 @@ TEST(Pipes, IndexMap) auto p4 = Entry{} >> Map({10L, 20L, 30L, 40L}, comp{}) >> Cap{}; - EXPECT_EQ(p4.entry_cast(29L), 2); - EXPECT_EQ(p4.entry_cast(25L), 0); + EXPECT_EQ(p4.entry_cast(29L), 2U); + EXPECT_EQ(p4.entry_cast(25L), 0U); } TEST(Pipes, OrderedMap) @@ -795,13 +795,13 @@ TEST(Pipes, Ref) auto& p1 = Entry{} >> Ref{}; p1.inject(0); - EXPECT_EQ(gc.size(), 1); + EXPECT_EQ(gc.size(), 1U); gc.destroy(); Group g; Entry{} >> Ref{g}; - EXPECT_EQ(gc.size(), 0); - EXPECT_EQ(g.size(), 1); + EXPECT_EQ(gc.size(), 0U); + EXPECT_EQ(g.size(), 1U); g.destroy(); int cnt = 0; @@ -815,7 +815,7 @@ TEST(Pipes, Ref) 2 >> p3; EXPECT_EQ(cnt, 4); - EXPECT_EQ(gc.size(), 2); + EXPECT_EQ(gc.size(), 2U); } } // namespace diff --git a/tests/test_poller.cpp b/tests/test_poller.cpp index 8a57ff6f..b22c14e9 100644 --- a/tests/test_poller.cpp +++ b/tests/test_poller.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2020-2023 Jochem Rutgers +// SPDX-FileCopyrightText: 2020-2025 Jochem Rutgers // // SPDX-License-Identifier: MPL-2.0 @@ -52,13 +52,13 @@ TEST(Poller, PollableZmqSocket) EXPECT_EQ(poller.add(preq), 0); auto const* res = &poller.poll(0); - EXPECT_EQ(res->size(), 0); + EXPECT_EQ(res->size(), 0U); EXPECT_EQ(errno, EAGAIN); zmq_send(req, "Hi", 2, 0); res = &poller.poll(0); - ASSERT_EQ(res->size(), 1); + ASSERT_EQ(res->size(), 1U); EXPECT_EQ(res->at(0)->revents, stored::Pollable::PollIn + 0); zmq_close(req); diff --git a/tests/test_protocol.cpp b/tests/test_protocol.cpp index efb7e793..8c4453a1 100644 --- a/tests/test_protocol.cpp +++ b/tests/test_protocol.cpp @@ -1,7 +1,8 @@ -// SPDX-FileCopyrightText: 2020-2024 Jochem Rutgers +// SPDX-FileCopyrightText: 2020-2025 Jochem Rutgers // // SPDX-License-Identifier: MPL-2.0 +#include "libstored/aes.h" #include "libstored/compress.h" #include "libstored/fifo.h" #include "libstored/protocol.h" @@ -28,12 +29,12 @@ TEST(AsciiEscapeLayer, Encode) ll.encoded().clear(); l.encode("123", 3); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "123"); ll.encoded().clear(); l.encode("123\x00", 4); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "123\x7f@"); ll.encoded().clear(); @@ -41,7 +42,7 @@ TEST(AsciiEscapeLayer, Encode) "123\r" "4", 5); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ( ll.encoded().at(0), "123\x7f" @@ -49,7 +50,7 @@ TEST(AsciiEscapeLayer, Encode) ll.encoded().clear(); l.encode("123\x7f", 4); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "123\x7f\x7f"); ll.encoded().clear(); @@ -57,7 +58,7 @@ TEST(AsciiEscapeLayer, Encode) "\x7f" "123\r", 5); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ( ll.encoded().at(0), "\x7f\x7f" @@ -74,19 +75,19 @@ TEST(AsciiEscapeLayer, Decode) DECODE(l, "123\x7f" "F"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), "123\x06"); ll.decoded().clear(); DECODE(l, "123\x7f"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), "123\x7f"); ll.decoded().clear(); DECODE(l, "\x7f" "A12\r3"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ( ll.decoded().at(0), "\x01" @@ -101,30 +102,30 @@ TEST(SegmentationLayer, SingleChunkEncode) ll.encoded().clear(); l.encode("123", 3); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "123E"); ll.encoded().clear(); l.encode("", 0); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "E"); ll.encoded().clear(); l.encode("1234567", 7); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "1234567E"); ll.encoded().clear(); l.encode("1234", 4, false); l.encode("567", 3, true); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "1234567E"); ll.encoded().clear(); l.encode("1234", 4, false); l.encode("567", 3, false); l.encode(); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "1234567E"); } @@ -136,19 +137,19 @@ TEST(SegmentationLayer, MultiChunkEncode) ll.encoded().clear(); l.encode("1234", 4); - EXPECT_EQ(ll.encoded().size(), 2); + EXPECT_EQ(ll.encoded().size(), 2U); EXPECT_EQ(ll.encoded().at(0), "123C"); EXPECT_EQ(ll.encoded().at(1), "4E"); ll.encoded().clear(); l.encode("12345", 5); - EXPECT_EQ(ll.encoded().size(), 2); + EXPECT_EQ(ll.encoded().size(), 2U); EXPECT_EQ(ll.encoded().at(0), "123C"); EXPECT_EQ(ll.encoded().at(1), "45E"); ll.encoded().clear(); l.encode("1234567890", 10); - EXPECT_EQ(ll.encoded().size(), 4); + EXPECT_EQ(ll.encoded().size(), 4U); EXPECT_EQ(ll.encoded().at(0), "123C"); EXPECT_EQ(ll.encoded().at(1), "456C"); EXPECT_EQ(ll.encoded().at(2), "789C"); @@ -159,7 +160,7 @@ TEST(SegmentationLayer, MultiChunkEncode) l.encode("67", 2, false); l.encode("89", 2, false); l.encode(); - EXPECT_EQ(ll.encoded().size(), 3); + EXPECT_EQ(ll.encoded().size(), 3U); EXPECT_EQ(ll.encoded().at(0), "123C"); EXPECT_EQ(ll.encoded().at(1), "456C"); EXPECT_EQ(ll.encoded().at(2), "789E"); @@ -173,17 +174,17 @@ TEST(SegmentationLayer, SingleChunkDecode) ll.decoded().clear(); DECODE(l, "123E"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), "123"); ll.decoded().clear(); DECODE(l, "E"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), ""); ll.decoded().clear(); DECODE(l, ""); - EXPECT_EQ(ll.decoded().size(), 0); + EXPECT_EQ(ll.decoded().size(), 0U); } TEST(SegmentationLayer, MultiChunkDecode) @@ -194,31 +195,31 @@ TEST(SegmentationLayer, MultiChunkDecode) ll.decoded().clear(); DECODE(l, "12345E"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), "12345"); ll.decoded().clear(); DECODE(l, "1234567890E"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), "1234567890"); ll.decoded().clear(); DECODE(l, "123C"); DECODE(l, "45E"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), "12345"); ll.decoded().clear(); DECODE(l, "123C"); DECODE(l, "456789E"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), "123456789"); ll.decoded().clear(); DECODE(l, "123C"); DECODE(l, "456789C"); DECODE(l, "E"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), "123456789"); } @@ -235,10 +236,10 @@ TEST(DebugArqLayer, SingleChunk) DECODE(l, "\x01" "123"); - EXPECT_EQ(top.decoded().size(), 1); + EXPECT_EQ(top.decoded().size(), 1U); EXPECT_EQ(top.decoded().at(0), "123"); top.encode("abc", 3); - EXPECT_EQ(bottom.encoded().size(), 1); + EXPECT_EQ(bottom.encoded().size(), 1U); EXPECT_EQ( bottom.encoded().at(0), std::string( "\x81" @@ -250,10 +251,10 @@ TEST(DebugArqLayer, SingleChunk) DECODE(l, "\x02" "123"); - EXPECT_EQ(top.decoded().size(), 1); + EXPECT_EQ(top.decoded().size(), 1U); EXPECT_EQ(top.decoded().at(0), "123"); top.encode("abc", 3); - EXPECT_EQ(bottom.encoded().size(), 1); + EXPECT_EQ(bottom.encoded().size(), 1U); EXPECT_EQ( bottom.encoded().at(0), std::string( "\x02" @@ -266,10 +267,10 @@ TEST(DebugArqLayer, SingleChunk) DECODE(l, "\x01" "123"); - EXPECT_EQ(top.decoded().size(), 1); + EXPECT_EQ(top.decoded().size(), 1U); EXPECT_EQ(top.decoded().at(0), "123"); top.encode("abc", 3); - EXPECT_EQ(bottom.encoded().size(), 2); + EXPECT_EQ(bottom.encoded().size(), 2U); EXPECT_EQ(bottom.encoded().at(0), std::string("\x80", 1)); EXPECT_EQ( bottom.encoded().at(1), std::string( @@ -283,11 +284,11 @@ TEST(DebugArqLayer, SingleChunk) DECODE(l, "\x40\x13" "123"); - EXPECT_EQ(top.decoded().size(), 1); + EXPECT_EQ(top.decoded().size(), 1U); EXPECT_EQ(top.decoded().at(0), "123"); top.encode("abc", 3, false); top.encode("def", 3); - EXPECT_EQ(bottom.encoded().size(), 2); + EXPECT_EQ(bottom.encoded().size(), 2U); EXPECT_EQ(bottom.encoded().at(0), "\x80"); EXPECT_EQ( bottom.encoded().at(1), @@ -312,12 +313,12 @@ TEST(DebugArqLayer, MultiChunk) DECODE(l, "\x04" "456"); - EXPECT_EQ(top.decoded().size(), 2); + EXPECT_EQ(top.decoded().size(), 2U); EXPECT_EQ(top.decoded().at(0), "123"); EXPECT_EQ(top.decoded().at(1), "456"); top.encode("abc", 3); top.encode("defg", 4); - EXPECT_EQ(bottom.encoded().size(), 3); + EXPECT_EQ(bottom.encoded().size(), 3U); EXPECT_EQ(bottom.encoded().at(0), "\x80"); EXPECT_EQ( bottom.encoded().at(1), @@ -336,13 +337,13 @@ TEST(DebugArqLayer, MultiChunk) DECODE(l, "\x06" "456"); - EXPECT_EQ(top.decoded().size(), 2); + EXPECT_EQ(top.decoded().size(), 2U); EXPECT_EQ(top.decoded().at(0), "123"); EXPECT_EQ(top.decoded().at(1), "456"); top.encode("abc", 3, false); top.encode("defg", 4); top.encode("hi", 2); - EXPECT_EQ(bottom.encoded().size(), 2); + EXPECT_EQ(bottom.encoded().size(), 2U); EXPECT_EQ( bottom.encoded().at(0), std::string( "\x03" @@ -370,7 +371,7 @@ TEST(DebugArqLayer, LostRequest) DECODE(l, "\x02" "456"); - EXPECT_EQ(top.decoded().size(), 2); + EXPECT_EQ(top.decoded().size(), 2U); EXPECT_EQ(top.decoded().at(0), "123"); EXPECT_EQ(top.decoded().at(1), "456"); // Assume last part is lost. @@ -378,15 +379,15 @@ TEST(DebugArqLayer, LostRequest) DECODE(l, "\x02" "456"); - EXPECT_EQ(top.decoded().size(), 2); + EXPECT_EQ(top.decoded().size(), 2U); DECODE(l, "\x04" "zzz"); - EXPECT_EQ(top.decoded().size(), 2); + EXPECT_EQ(top.decoded().size(), 2U); DECODE(l, "\x20" "..."); - EXPECT_EQ(top.decoded().size(), 2); + EXPECT_EQ(top.decoded().size(), 2U); // Reset and retransmit full request DECODE(l, "\x80"); DECODE(l, @@ -398,13 +399,13 @@ TEST(DebugArqLayer, LostRequest) DECODE(l, "\x03" "789"); - EXPECT_EQ(top.decoded().size(), 5); + EXPECT_EQ(top.decoded().size(), 5U); EXPECT_EQ(top.decoded().at(2), "123"); EXPECT_EQ(top.decoded().at(3), "456"); EXPECT_EQ(top.decoded().at(4), "789"); top.encode("abc", 3); - EXPECT_EQ(bottom.encoded().size(), 2); + EXPECT_EQ(bottom.encoded().size(), 2U); EXPECT_EQ(bottom.encoded().at(0), "\x80"); EXPECT_EQ( bottom.encoded().at(1), std::string( @@ -415,7 +416,7 @@ TEST(DebugArqLayer, LostRequest) top.decoded().clear(); bottom.encoded().clear(); DECODE(l, "\x80"); - EXPECT_EQ(bottom.encoded().size(), 1); + EXPECT_EQ(bottom.encoded().size(), 1U); EXPECT_EQ(bottom.encoded().at(0), "\x80"); DECODE(l, "\x01" @@ -423,7 +424,7 @@ TEST(DebugArqLayer, LostRequest) DECODE(l, "\x02" "456"); - EXPECT_EQ(top.decoded().size(), 2); + EXPECT_EQ(top.decoded().size(), 2U); EXPECT_EQ(top.decoded().at(0), "123"); EXPECT_EQ(top.decoded().at(1), "456"); // Do some retransmit @@ -442,7 +443,7 @@ TEST(DebugArqLayer, LostRequest) DECODE(l, "\x03" "567"); - EXPECT_EQ(top.decoded().size(), 3); + EXPECT_EQ(top.decoded().size(), 3U); EXPECT_EQ(top.decoded().at(2), "567"); } @@ -457,17 +458,17 @@ TEST(DebugArqLayer, LostResponse) top.decoded().clear(); bottom.encoded().clear(); DECODE(l, "\x8F"); - EXPECT_EQ(bottom.encoded().size(), 1); + EXPECT_EQ(bottom.encoded().size(), 1U); EXPECT_EQ(bottom.encoded().at(0), "\x80"); bottom.encoded().clear(); DECODE(l, "\x10" "123"); - EXPECT_EQ(top.decoded().size(), 1); + EXPECT_EQ(top.decoded().size(), 1U); EXPECT_EQ(top.decoded().at(0), "123"); top.encode("abc", 3); - EXPECT_EQ(bottom.encoded().size(), 1); + EXPECT_EQ(bottom.encoded().size(), 1U); EXPECT_EQ( bottom.encoded().at(0), std::string( "\x01" @@ -478,8 +479,8 @@ TEST(DebugArqLayer, LostResponse) DECODE(l, "\x10" "123"); - EXPECT_EQ(top.decoded().size(), 1); - EXPECT_EQ(bottom.encoded().size(), 2); + EXPECT_EQ(top.decoded().size(), 1U); + EXPECT_EQ(bottom.encoded().size(), 2U); EXPECT_EQ( bottom.encoded().at(1), std::string( "\x01" @@ -489,12 +490,12 @@ TEST(DebugArqLayer, LostResponse) DECODE(l, "\x11" "456"); - EXPECT_EQ(top.decoded().size(), 2); + EXPECT_EQ(top.decoded().size(), 2U); EXPECT_EQ(top.decoded().at(1), "456"); top.encode("def", 3, false); top.encode("g", 1); top.encode("hi", 2); - EXPECT_EQ(bottom.encoded().size(), 4); + EXPECT_EQ(bottom.encoded().size(), 4U); EXPECT_EQ( bottom.encoded().at(2), std::string( "\x02" @@ -507,7 +508,7 @@ TEST(DebugArqLayer, LostResponse) DECODE(l, "\x11" "456"); - EXPECT_EQ(bottom.encoded().size(), 6); + EXPECT_EQ(bottom.encoded().size(), 6U); EXPECT_EQ( bottom.encoded().at(4), std::string( "\x02" @@ -530,18 +531,18 @@ TEST(DebugArqLayer, Purgeable) top.decoded().clear(); bottom.encoded().clear(); DECODE(l, "\x80"); - EXPECT_EQ(bottom.encoded().size(), 1); + EXPECT_EQ(bottom.encoded().size(), 1U); EXPECT_EQ(bottom.encoded().at(0), "\x80"); bottom.encoded().clear(); DECODE(l, "\x01" "123"); - EXPECT_EQ(top.decoded().size(), 1); + EXPECT_EQ(top.decoded().size(), 1U); EXPECT_EQ(top.decoded().at(0), "123"); top.setPurgeableResponse(); top.encode("abc", 3); - EXPECT_EQ(bottom.encoded().size(), 1); + EXPECT_EQ(bottom.encoded().size(), 1U); EXPECT_EQ( bottom.encoded().at(0), std::string( "\x01" @@ -552,11 +553,11 @@ TEST(DebugArqLayer, Purgeable) DECODE(l, "\x01" "123"); - EXPECT_EQ(top.decoded().size(), 2); + EXPECT_EQ(top.decoded().size(), 2U); EXPECT_EQ(top.decoded().at(1), "123"); top.setPurgeableResponse(); top.encode("def", 3); - EXPECT_EQ(bottom.encoded().size(), 2); + EXPECT_EQ(bottom.encoded().size(), 2U); EXPECT_EQ( bottom.encoded().at(1), std::string( "\x82" @@ -567,11 +568,11 @@ TEST(DebugArqLayer, Purgeable) DECODE(l, "\x01" "123"); - EXPECT_EQ(top.decoded().size(), 3); + EXPECT_EQ(top.decoded().size(), 3U); EXPECT_EQ(top.decoded().at(2), "123"); // Default to precious, but reset flag remains. top.encode("ghi", 3); - EXPECT_EQ(bottom.encoded().size(), 3); + EXPECT_EQ(bottom.encoded().size(), 3U); EXPECT_EQ( bottom.encoded().at(2), std::string( "\x83" @@ -581,9 +582,9 @@ TEST(DebugArqLayer, Purgeable) DECODE(l, "\x01" "123"); - EXPECT_EQ(top.decoded().size(), 3); + EXPECT_EQ(top.decoded().size(), 3U); // Default to precious, but reset flag remains. - EXPECT_EQ(bottom.encoded().size(), 4); + EXPECT_EQ(bottom.encoded().size(), 4U); EXPECT_EQ( bottom.encoded().at(3), std::string( "\x83" @@ -593,10 +594,10 @@ TEST(DebugArqLayer, Purgeable) DECODE(l, "\x02" "123"); - EXPECT_EQ(top.decoded().size(), 4); + EXPECT_EQ(top.decoded().size(), 4U); // Default to precious. top.encode("jkl", 3); - EXPECT_EQ(bottom.encoded().size(), 5); + EXPECT_EQ(bottom.encoded().size(), 5U); EXPECT_EQ( bottom.encoded().at(4), std::string( "\x04" @@ -615,15 +616,15 @@ TEST(DebugArqLayer, Overflow) top.decoded().clear(); bottom.encoded().clear(); DECODE(l, "\x80"); - EXPECT_EQ(bottom.encoded().size(), 1); + EXPECT_EQ(bottom.encoded().size(), 1U); EXPECT_EQ(bottom.encoded().at(0), "\x80"); bottom.encoded().clear(); DECODE(l, "\x01" "123"); - EXPECT_EQ(top.decoded().size(), 1); + EXPECT_EQ(top.decoded().size(), 1U); top.encode("abcde", 5); - EXPECT_EQ(bottom.encoded().size(), 1); + EXPECT_EQ(bottom.encoded().size(), 1U); EXPECT_EQ( bottom.encoded().at(0), std::string( "\x01" @@ -634,9 +635,9 @@ TEST(DebugArqLayer, Overflow) "\x01" "123"); // Behave like purgeable. - EXPECT_EQ(top.decoded().size(), 2); + EXPECT_EQ(top.decoded().size(), 2U); top.encode("fghij", 5); - EXPECT_EQ(bottom.encoded().size(), 2); + EXPECT_EQ(bottom.encoded().size(), 2U); EXPECT_EQ( bottom.encoded().at(1), std::string( "\x82" @@ -646,9 +647,9 @@ TEST(DebugArqLayer, Overflow) DECODE(l, "\x02" "456"); - EXPECT_EQ(top.decoded().size(), 3); + EXPECT_EQ(top.decoded().size(), 3U); top.encode("klm", 3); - EXPECT_EQ(bottom.encoded().size(), 3); + EXPECT_EQ(bottom.encoded().size(), 3U); EXPECT_EQ( bottom.encoded().at(2), std::string( "\x03" @@ -658,8 +659,8 @@ TEST(DebugArqLayer, Overflow) DECODE(l, "\x02" "456"); - EXPECT_EQ(top.decoded().size(), 3); - EXPECT_EQ(bottom.encoded().size(), 4); + EXPECT_EQ(top.decoded().size(), 3U); + EXPECT_EQ(bottom.encoded().size(), 4U); EXPECT_EQ( bottom.encoded().at(3), std::string( "\x03" @@ -675,22 +676,22 @@ TEST(Crc8Layer, Encode) ll.encoded().clear(); l.encode(); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "\xff"); ll.encoded().clear(); l.encode("1", 1); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "1\x5e"); ll.encoded().clear(); l.encode("12", 2); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "12\x54"); ll.encoded().clear(); l.encode("123", 3); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "123\xfc"); } @@ -702,33 +703,33 @@ TEST(Crc8Layer, Decode) ll.decoded().clear(); DECODE(l, "\xff"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), ""); ll.decoded().clear(); DECODE(l, "1\x5e"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), "1"); ll.decoded().clear(); DECODE(l, "12\x54"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), "12"); ll.decoded().clear(); DECODE(l, "123\xfc"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), "123"); ll.decoded().clear(); DECODE(l, "1234\xfc"); - EXPECT_EQ(ll.decoded().size(), 0); + EXPECT_EQ(ll.decoded().size(), 0U); ll.decoded().clear(); DECODE(l, "\x00" "123\xfc"); - EXPECT_EQ(ll.decoded().size(), 0); + EXPECT_EQ(ll.decoded().size(), 0U); } TEST(Crc16Layer, Encode) @@ -739,22 +740,22 @@ TEST(Crc16Layer, Encode) ll.encoded().clear(); l.encode(); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "\xff\xff"); ll.encoded().clear(); l.encode("1", 1); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "1\x49\xD6"); ll.encoded().clear(); l.encode("12", 2); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "12\x77\xA2"); ll.encoded().clear(); l.encode("123", 3); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "123\x1C\x84"); } @@ -766,33 +767,33 @@ TEST(Crc16Layer, Decode) ll.decoded().clear(); DECODE(l, "\xff\xff"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), ""); ll.decoded().clear(); DECODE(l, "1\x49\xd6"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), "1"); ll.decoded().clear(); DECODE(l, "12\x77\xa2"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), "12"); ll.decoded().clear(); DECODE(l, "123\x1c\x84"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), "123"); ll.decoded().clear(); DECODE(l, "1234\x1c\x84"); - EXPECT_EQ(ll.decoded().size(), 0); + EXPECT_EQ(ll.decoded().size(), 0U); ll.decoded().clear(); DECODE(l, "\x00" "123\x1c\x84"); - EXPECT_EQ(ll.decoded().size(), 0); + EXPECT_EQ(ll.decoded().size(), 0U); } TEST(Crc32Layer, Encode) @@ -803,7 +804,7 @@ TEST(Crc32Layer, Encode) ll.encoded().clear(); l.encode(); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0)[0], '\x00'); EXPECT_EQ(ll.encoded().at(0)[1], '\x00'); EXPECT_EQ(ll.encoded().at(0)[2], '\x00'); @@ -811,22 +812,22 @@ TEST(Crc32Layer, Encode) ll.encoded().clear(); l.encode("1", 1); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "1\x83\xDC\xEF\xB7"); ll.encoded().clear(); l.encode("12", 2); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "12OSD\xCD"); ll.encoded().clear(); l.encode("123", 3); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "123\x88Hc\xD2"); ll.encoded().clear(); l.encode("1234", 4); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "1234\x9B\xE3\xE0\xA3"); } @@ -838,37 +839,37 @@ TEST(Crc32Layer, Decode) ll.decoded().clear(); DECODE(l, "\0\0\0\0"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), ""); ll.decoded().clear(); DECODE(l, "1\x83\xDC\xEF\xB7"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), "1"); ll.decoded().clear(); DECODE(l, "12OSD\xCD"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), "12"); ll.decoded().clear(); DECODE(l, "123\x88Hc\xD2"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), "123"); ll.decoded().clear(); DECODE(l, "1234\x9B\xE3\xE0\xA3"); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); ll.decoded().clear(); DECODE(l, "123\x9B\xE3\xE0\xA3"); - EXPECT_EQ(ll.decoded().size(), 0); + EXPECT_EQ(ll.decoded().size(), 0U); ll.decoded().clear(); DECODE(l, "\x00" "1234\x9B\xE3\xE0\xA3"); - EXPECT_EQ(ll.decoded().size(), 0); + EXPECT_EQ(ll.decoded().size(), 0U); } TEST(BufferLayer, Encode) @@ -879,47 +880,47 @@ TEST(BufferLayer, Encode) ll.encoded().clear(); l.encode("123", 3); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "123"); ll.encoded().clear(); l.encode("12", 2, false); l.encode("3", 1); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "123"); ll.encoded().clear(); l.encode("12", 2, false); l.encode("3", 1, false); l.encode(); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "123"); ll.encoded().clear(); l.encode("1234", 4, true); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "1234"); ll.encoded().clear(); l.encode("1234", 4, false); l.encode(); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "1234"); ll.encoded().clear(); l.encode("12345", 5, true); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "12345"); ll.encoded().clear(); l.encode("12345", 5, false); l.encode("67", 2, true); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "1234567"); ll.encoded().clear(); l.encode("1234567890", 10, true); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "1234567890"); } @@ -980,7 +981,7 @@ TEST(ArqLayer, Retransmit) DECODE(bottom, "\x80"); top.flush(); // no retransmit - EXPECT_EQ(bottom.encoded().size(), 3); + EXPECT_EQ(bottom.encoded().size(), 3U); top.clear(); bottom.clear(); @@ -989,7 +990,7 @@ TEST(ArqLayer, Retransmit) EXPECT_EQ(bottom.encoded().at(0), "\x01 1"); top.encode(" 2", 2); // Does not retransmit. - EXPECT_EQ(bottom.encoded().size(), 1); + EXPECT_EQ(bottom.encoded().size(), 1U); l.keepAlive(); // triggers retransmit of 1 EXPECT_EQ(bottom.encoded().at(1), "\x01 1"); @@ -1002,7 +1003,7 @@ TEST(ArqLayer, Retransmit) // Wrong ack DECODE(bottom, "\x83"); // ignored - EXPECT_EQ(bottom.encoded().size(), 4); + EXPECT_EQ(bottom.encoded().size(), 4U); DECODE(bottom, "\x82"); top.clear(); @@ -1043,7 +1044,7 @@ TEST(ArqLayer, KeepAlive) DECODE(bottom, "\x82"); DECODE(bottom, "\x41"); - EXPECT_EQ(top.decoded().size(), 0); + EXPECT_EQ(top.decoded().size(), 0U); EXPECT_EQ(bottom.encoded().at(3), "\x81"); } @@ -1241,7 +1242,7 @@ TEST(FileLayer, NamedPipe) l.encode("Zip-a-Dee-Doo-Dah", 17); char buf[32] = {}; - EXPECT_EQ(read(fd, buf, sizeof(buf)), 17); + EXPECT_EQ(read(fd, buf, sizeof(buf)), 17U); EXPECT_EQ(std::string(buf), "Zip-a-Dee-Doo-Dah"); close(fd); @@ -1321,7 +1322,7 @@ TEST(FifoLoopback1, FifoLoopback1) l.encode("night", 5); EXPECT_EQ(l.recv(), 0); - EXPECT_EQ(top.decoded().size(), 1); + EXPECT_EQ(top.decoded().size(), 1U); EXPECT_EQ(top.decoded().at(0), "This is the night"); l.encode("It's a beautiful night", 22); @@ -1329,11 +1330,11 @@ TEST(FifoLoopback1, FifoLoopback1) l.encode("bella notte", 11, false); l.encode(); - EXPECT_EQ(top.decoded().size(), 1); + EXPECT_EQ(top.decoded().size(), 1U); EXPECT_EQ(l.recv(), 0); EXPECT_EQ(l.recv(), 0); EXPECT_EQ(l.recv(), EAGAIN); - EXPECT_EQ(top.decoded().size(), 3); + EXPECT_EQ(top.decoded().size(), 3U); } TEST(FifoLoopback, FifoLoopback) @@ -1347,15 +1348,15 @@ TEST(FifoLoopback, FifoLoopback) a.encode("the ", 4); EXPECT_EQ(l.b2a().recv(), EAGAIN); b.encode("skies", 5); - EXPECT_EQ(a.decoded().size(), 0); - EXPECT_EQ(b.decoded().size(), 0); + EXPECT_EQ(a.decoded().size(), 0U); + EXPECT_EQ(b.decoded().size(), 0U); EXPECT_EQ(l.a2b().recv(), 0); - EXPECT_EQ(b.decoded().size(), 1); + EXPECT_EQ(b.decoded().size(), 1U); EXPECT_EQ(b.decoded().at(0), "Look the "); EXPECT_EQ(l.b2a().recv(), 0); - EXPECT_EQ(a.decoded().size(), 1); + EXPECT_EQ(a.decoded().size(), 1U); EXPECT_EQ(a.decoded().at(0), "at skies"); EXPECT_EQ(l.a2b().lastError(), 0); @@ -1422,11 +1423,11 @@ TEST(TerminalLayer, Encode) ll.encoded().clear(); l.encode("You can learn a lot", 19); - EXPECT_EQ(ll.encoded().size(), 1); + EXPECT_EQ(ll.encoded().size(), 1U); EXPECT_EQ(ll.encoded().at(0), "\x1b_You can learn a lot\x1b\\"); l.nonDebugEncode("of things", 9); - EXPECT_EQ(ll.encoded().size(), 2); + EXPECT_EQ(ll.encoded().size(), 2U); EXPECT_EQ(ll.encoded().at(1), "of things"); } @@ -1440,7 +1441,7 @@ TEST(TerminalLayer, Decode) DECODE(l, "from the \x1b_flowers\x1b\\..."); EXPECT_EQ(nonDebug, "from the ..."); - EXPECT_EQ(ll.decoded().size(), 1); + EXPECT_EQ(ll.decoded().size(), 1U); EXPECT_EQ(ll.decoded().at(0), "flowers"); } @@ -1476,7 +1477,7 @@ TEST(MuxLayer, Decode) stored::MuxLayer l{{ch0, ch1, ch2}}; DECODE(l, "ch?"); - EXPECT_EQ(ch0.decoded().size(), 0); + EXPECT_EQ(ch0.decoded().size(), 0U); DECODE(l, "\x10\x00 ch0"); EXPECT_EQ(ch0.decoded().at(0), " ch0"); @@ -1502,8 +1503,89 @@ TEST(MuxLayer, Decode) EXPECT_EQ(ch0.decoded().at(2), " 0 1"); DECODE(l, "\x10\x03 ch?"); - EXPECT_EQ(ch0.decoded().size(), 3); + EXPECT_EQ(ch0.decoded().size(), 3U); } +TEST(Aes256Layer, EncodeDecode) +{ + std::string key = "some very secure key of 32 bytes"; + ASSERT_EQ(key.size(), stored::Aes256Layer::KeySize); + + stored::Aes256Layer a; + a.setKey((uint8_t const*)key.data()); + LoggingLayer la; + la.stack(a); + +#if 0 + // Print encrypted messages. + stored::PrintLayer p; + p.wrap(a); +#else + auto& p = a; +#endif + + stored::Aes256Layer b((uint8_t const*)key.data()); + LoggingLayer lb; + lb.stack(b); + + stored::Loopback l(p, b); + + // Normal encode/decode. + la.encode("1", 1); + EXPECT_EQ(lb.decoded().at(0), "1"); + EXPECT_EQ(a.lastError(), 0); + EXPECT_EQ(b.lastError(), 0); + + lb.encode("2", 1); + EXPECT_EQ(la.decoded().at(0), "2"); + + la.encode("3", 1); + EXPECT_EQ(lb.decoded().at(1), "3"); + + la.encode("0123456789abcdef", 16); + EXPECT_EQ(lb.decoded().at(2), "0123456789abcdef"); + + la.encode("0123456789abcdef0", 17); + EXPECT_EQ(lb.decoded().at(3), "0123456789abcdef0"); + + lb.encode("xyz", 3); + EXPECT_EQ(la.decoded().at(1), "xyz"); + EXPECT_EQ(a.lastError(), 0); + EXPECT_EQ(b.lastError(), 0); + + // Reconnect. + la.clear(); + lb.clear(); + a.connected(); + b.connected(); + + la.encode("abc", 3); + EXPECT_EQ(lb.decoded().at(0), "abc"); + EXPECT_EQ(a.lastError(), 0); + EXPECT_EQ(b.lastError(), 0); + + lb.encode("def", 3); + EXPECT_EQ(la.decoded().at(0), "def"); + + // Change key. + key = "another secure key of 32 bytes "; + ASSERT_EQ(key.size(), stored::Aes256Layer::KeySize); + a.setKey((uint8_t const*)key.data()); + b.setKey((uint8_t const*)key.data()); + + la.encode("ghij", 4); + EXPECT_EQ(lb.decoded().at(1), "ghij"); + + lb.encode("klmn", 4); + EXPECT_EQ(la.decoded().at(1), "klmn"); + + // Set unified. + a.unified(true); + la.encode("op", 2); + EXPECT_EQ(lb.decoded().at(2), "op"); + + lb.encode("qr", 2); + EXPECT_EQ(la.decoded().at(2), "qr"); +} } // namespace diff --git a/tests/test_spm.cpp b/tests/test_spm.cpp index f2509e0b..e0836821 100644 --- a/tests/test_spm.cpp +++ b/tests/test_spm.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2020-2023 Jochem Rutgers +// SPDX-FileCopyrightText: 2020-2025 Jochem Rutgers // // SPDX-License-Identifier: MPL-2.0 @@ -11,14 +11,14 @@ TEST(ScrachPad, Alloc) { stored::ScratchPad<> spm; - EXPECT_EQ(spm.chunks(), 0); - EXPECT_EQ(spm.size(), 0); - EXPECT_EQ(spm.max(), 0); + EXPECT_EQ(spm.chunks(), 0U); + EXPECT_EQ(spm.size(), 0U); + EXPECT_EQ(spm.max(), 0U); // First chunk alloc. void** a = spm.alloc(); EXPECT_NE(a, nullptr); - EXPECT_EQ(spm.chunks(), 1); + EXPECT_EQ(spm.chunks(), 1U); EXPECT_EQ(spm.size(), sizeof(void*)); EXPECT_EQ(spm.max(), sizeof(void*)); EXPECT_GE(spm.capacity(), sizeof(void*)); @@ -27,9 +27,9 @@ TEST(ScrachPad, Alloc) void** b = spm.alloc(); EXPECT_NE(b, nullptr); EXPECT_NE(a, b); - EXPECT_EQ(spm.size(), sizeof(void*) * 2); - EXPECT_EQ(spm.max(), sizeof(void*) * 2); - EXPECT_GE(spm.capacity(), sizeof(void*) * 2); + EXPECT_EQ(spm.size(), sizeof(void*) * 2U); + EXPECT_EQ(spm.max(), sizeof(void*) * 2U); + EXPECT_GE(spm.capacity(), sizeof(void*) * 2U); // Random allocs. EXPECT_NE(spm.alloc(10), nullptr); @@ -51,9 +51,9 @@ TEST(ScratchPad, Reset) // Empty reset. spm.reset(); - EXPECT_EQ(spm.chunks(), 0); - EXPECT_EQ(spm.size(), 0); - EXPECT_EQ(spm.max(), 0); + EXPECT_EQ(spm.chunks(), 0U); + EXPECT_EQ(spm.size(), 0U); + EXPECT_EQ(spm.max(), 0U); // First chunk reset. int* i = spm.alloc(); @@ -61,8 +61,8 @@ TEST(ScratchPad, Reset) *i = 42; spm.reset(); spmInfo(spm); - EXPECT_EQ(spm.chunks(), 1); - EXPECT_EQ(spm.size(), 0); + EXPECT_EQ(spm.chunks(), 1U); + EXPECT_EQ(spm.size(), 0U); EXPECT_EQ(spm.max(), sizeof(int)); // Force an additional chunk. @@ -73,11 +73,11 @@ TEST(ScratchPad, Reset) size_t total = spm.size(); spmInfo(spm); EXPECT_NE(p, nullptr); - EXPECT_EQ(spm.chunks(), 2); + EXPECT_EQ(spm.chunks(), 2U); spm.reset(); spmInfo(spm); - EXPECT_EQ(spm.chunks(), 1); - EXPECT_EQ(spm.size(), 0); + EXPECT_EQ(spm.chunks(), 1U); + EXPECT_EQ(spm.size(), 0U); EXPECT_GE(spm.capacity(), total); } @@ -93,8 +93,8 @@ TEST(ScratchPad, Alignment) // Add padding bytes for int int* i = spm.alloc(); EXPECT_NE(i, nullptr); - EXPECT_EQ((uintptr_t)i & (sizeof(int) - 1), 0); - EXPECT_EQ(spm.size(), sizeof(int) * 2); + EXPECT_EQ((uintptr_t)i & (sizeof(int) - 1), 0U); + EXPECT_EQ(spm.size(), sizeof(int) * 2U); // Another few bytes c = spm.alloc(); @@ -107,7 +107,7 @@ TEST(ScratchPad, Alignment) // More padding for double double* d = spm.alloc(); EXPECT_NE(d, nullptr); - EXPECT_EQ((uintptr_t)d & (sizeof(double) - 1), 0); + EXPECT_EQ((uintptr_t)d & (sizeof(double) - 1), 0U); EXPECT_EQ(spm.size(), sizeof(int) * 2 + sizeof(void*) + sizeof(double)); } @@ -118,29 +118,29 @@ TEST(ScratchPad, Snapshot) auto c = spm.alloc(); EXPECT_NE(c, nullptr); - EXPECT_EQ(spm.size(), 1); + EXPECT_EQ(spm.size(), 1U); // Rollback within same chunk. auto s1 = spm.snapshot(); c = spm.alloc(); EXPECT_NE(c, nullptr); - EXPECT_EQ(spm.size(), 2); + EXPECT_EQ(spm.size(), 2U); s1.rollback(); - EXPECT_EQ(spm.size(), 1); + EXPECT_EQ(spm.size(), 1U); auto d = spm.alloc(); EXPECT_NE(d, nullptr); EXPECT_EQ(spm.size(), sizeof(void*) + sizeof(double)); s1.rollback(); - EXPECT_EQ(spm.size(), 1); + EXPECT_EQ(spm.size(), 1U); // Rollback to previous chunk. c = spm.alloc(spm.capacity() - spm.size() + 1); EXPECT_NE(c, nullptr); - EXPECT_EQ(spm.chunks(), 2); + EXPECT_EQ(spm.chunks(), 2U); s1.rollback(); - EXPECT_EQ(spm.size(), 1); - EXPECT_EQ(spm.chunks(), 1); + EXPECT_EQ(spm.size(), 1U); + EXPECT_EQ(spm.chunks(), 1U); } TEST(ScratchPad, Shrink) @@ -160,16 +160,16 @@ TEST(ScratchPad, Shrink) auto c = spm.alloc(spm.capacity() + 1); EXPECT_NE(c, nullptr); - EXPECT_EQ(spm.chunks(), 2); + EXPECT_EQ(spm.chunks(), 2U); s.rollback(); s.reset(); - EXPECT_EQ(spm.chunks(), 1); + EXPECT_EQ(spm.chunks(), 1U); spm.reset(); - EXPECT_EQ(spm.chunks(), 1); + EXPECT_EQ(spm.chunks(), 1U); spm.shrink_to_fit(); - EXPECT_EQ(spm.chunks(), 0); - EXPECT_EQ(spm.capacity(), 0); + EXPECT_EQ(spm.chunks(), 0U); + EXPECT_EQ(spm.capacity(), 0U); } TEST(ScratchPad, Stress) diff --git a/tests/test_synchronizer.cpp b/tests/test_synchronizer.cpp index 1cda3af0..308d1f6e 100644 --- a/tests/test_synchronizer.cpp +++ b/tests/test_synchronizer.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2020-2023 Jochem Rutgers +// SPDX-FileCopyrightText: 2020-2025 Jochem Rutgers // // SPDX-License-Identifier: MPL-2.0 @@ -25,20 +25,20 @@ namespace { TEST(Synchronizer, Endianness) { - EXPECT_EQ(stored::swap_endian(1), 1); - EXPECT_EQ(stored::swap_endian(0x1234), 0x3412); - EXPECT_EQ(stored::swap_endian(0x12345678), 0x78563412); + EXPECT_EQ(stored::swap_endian(1), 1U); + EXPECT_EQ(stored::swap_endian(0x1234), 0x3412U); + EXPECT_EQ(stored::swap_endian(0x12345678), 0x78563412U); uint8_t b[] = {1, 2, 3}; stored::swap_endian_<3>(b); - EXPECT_EQ(b[0], 3); - EXPECT_EQ(b[1], 2); - EXPECT_EQ(b[2], 1); + EXPECT_EQ(b[0], 3U); + EXPECT_EQ(b[1], 2U); + EXPECT_EQ(b[2], 1U); stored::swap_endian(b, 2); - EXPECT_EQ(b[0], 2); - EXPECT_EQ(b[1], 3); - EXPECT_EQ(b[2], 1); + EXPECT_EQ(b[0], 2U); + EXPECT_EQ(b[1], 3U); + EXPECT_EQ(b[2], 1U); } TEST(Synchronizer, Instantiate) @@ -66,7 +66,7 @@ TEST(Synchronizer, ShortSeq) { TestJournal j("123", nullptr, 0u); - EXPECT_EQ(j.seq(), 1); + EXPECT_EQ(j.seq(), 1U); j.changed(1, 0); EXPECT_TRUE(j.hasChanged(1, 1)); @@ -74,29 +74,29 @@ TEST(Synchronizer, ShortSeq) for(int i = 1; i < 50; i++) j.bumpSeq(true); - EXPECT_EQ(j.seq(), 50); + EXPECT_EQ(j.seq(), 50U); EXPECT_FALSE(j.hasChanged(1, 2)); - EXPECT_EQ(j.toShort(50), 50); - EXPECT_EQ(j.toShort(49), 49); - EXPECT_EQ(j.toShort(1), 1); + EXPECT_EQ(j.toShort(50), 50U); + EXPECT_EQ(j.toShort(49), 49U); + EXPECT_EQ(j.toShort(1), 1U); - EXPECT_EQ(j.toLong(50), 50); - EXPECT_EQ(j.toLong(49), 49); - EXPECT_EQ(j.toLong(1), 1); + EXPECT_EQ(j.toLong(50), 50U); + EXPECT_EQ(j.toLong(49), 49U); + EXPECT_EQ(j.toLong(1), 1U); for(int i = 0; i < 0x10000; i++) j.bumpSeq(true); - EXPECT_EQ(j.toShort(0x10032), 50); - EXPECT_EQ(j.toShort(0x10031), 49); - EXPECT_EQ(j.toShort(0x10001), 1); - EXPECT_EQ(j.toShort(51), 51); + EXPECT_EQ(j.toShort(0x10032), 50U); + EXPECT_EQ(j.toShort(0x10031), 49U); + EXPECT_EQ(j.toShort(0x10001), 1U); + EXPECT_EQ(j.toShort(51), 51U); - EXPECT_EQ(j.toLong(51), 51); - EXPECT_EQ(j.toLong(50), 0x10032); - EXPECT_EQ(j.toLong(49), 0x10031); - EXPECT_EQ(j.toLong(1), 0x10001); + EXPECT_EQ(j.toLong(51), 51U); + EXPECT_EQ(j.toLong(50), 0x10032U); + EXPECT_EQ(j.toLong(49), 0x10031U); + EXPECT_EQ(j.toLong(1), 0x10001U); EXPECT_TRUE(j.hasChanged( 1, j.seq() - TestJournal::ShortSeqWindow + TestJournal::SeqLowerMargin)); @@ -113,7 +113,7 @@ TEST(Synchronizer, Changes) size_t c = 0; store.journal().iterateChanged(0, [&](stored::StoreJournal::Key) { c++; }); - EXPECT_EQ(c, 0); + EXPECT_EQ(c, 0U); stored::StoreJournal::Key key_u8 = (stored::StoreJournal::Key)u8.key(); EXPECT_FALSE(store.journal().hasChanged(key_u8, now)); @@ -123,7 +123,7 @@ TEST(Synchronizer, Changes) c = 0; store.journal().iterateChanged(0, [&](stored::StoreJournal::Key) { c++; }); - EXPECT_EQ(c, 1); + EXPECT_EQ(c, 1U); now = store.journal().seq(); store.default_uint8 = 2; @@ -144,29 +144,29 @@ TEST(Synchronizer, Changes) c = 0; store.journal().iterateChanged(0, [&](stored::StoreJournal::Key) { c++; }); - EXPECT_EQ(c, 2); + EXPECT_EQ(c, 2U); } -#define EXPECT_SYNCED(store1, store2) \ - do { \ - auto _map1 = (store1).map(); \ - auto _map2 = (store2).map(); \ - for(auto& _o : _map1) \ - EXPECT_EQ(_o.second.get(), _map2[_o.first].get()); \ - } while(0) - -#define EXPECT_NOT_SYNCED(store1, store2) \ - do { \ - auto _map1 = (store1).map(); \ - auto _map2 = (store2).map(); \ - bool _synced = true; \ - for(auto& _o : _map1) \ - if(_o.second.get() != _map2[_o.first].get()) { \ - _synced = false; \ - break; \ - } \ - EXPECT_FALSE(_synced); \ - } while(0) +#define EXPECT_SYNCED(store1, store2) \ + do { \ + auto _map1 = (store1).map(); \ + auto _map2 = (store2).map(); \ + for(auto& _o : _map1) \ + EXPECT_EQ(_o.second.get(), _map2[_o.first].get()); \ + } while(0) + +#define EXPECT_NOT_SYNCED(store1, store2) \ + do { \ + auto _map1 = (store1).map(); \ + auto _map2 = (store2).map(); \ + bool _synced = true; \ + for(auto& _o : _map1) \ + if(_o.second.get() != _map2[_o.first].get()) { \ + _synced = false; \ + break; \ + } \ + EXPECT_FALSE(_synced); \ + } while(0) TEST(Synchronizer, Sync2) { diff --git a/tests/test_types.cpp b/tests/test_types.cpp index 6615ae64..cd727b24 100644 --- a/tests/test_types.cpp +++ b/tests/test_types.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: 2020-2023 Jochem Rutgers +// SPDX-FileCopyrightText: 2020-2025 Jochem Rutgers // // SPDX-License-Identifier: MPL-2.0 @@ -8,12 +8,12 @@ #include "gtest/gtest.h" #ifdef STORED_OS_WINDOWS -# include -# ifndef alloca -# define alloca(s) _malloca(s) -# endif +# include +# ifndef alloca +# define alloca(s) _malloca(s) +# endif #else -# include +# include #endif namespace { @@ -55,47 +55,47 @@ TEST(Types, Int64) TEST(Types, Uint8) { stored::TestStore store; - EXPECT_EQ(store.default_uint8.get(), 0); - store.default_uint8 = 42; - EXPECT_EQ(store.default_uint8.get(), 42); + EXPECT_EQ(store.default_uint8.get(), 0U); + store.default_uint8 = 42U; + EXPECT_EQ(store.default_uint8.get(), 42U); } TEST(Types, Uint16) { stored::TestStore store; - EXPECT_EQ(store.default_uint16.get(), 0); - store.default_uint16 = 0x1234; - EXPECT_EQ(store.default_uint16.get(), 0x1234); + EXPECT_EQ(store.default_uint16.get(), 0U); + store.default_uint16 = 0x1234U; + EXPECT_EQ(store.default_uint16.get(), 0x1234U); } TEST(Types, Uint32) { stored::TestStore store; - EXPECT_EQ(store.default_uint32.get(), 0); - store.default_uint32 = 0x8abcdef0; - EXPECT_EQ(store.default_uint32.get(), 0x8abcdef0); + EXPECT_EQ(store.default_uint32.get(), 0U); + store.default_uint32 = 0x8abcdef0U; + EXPECT_EQ(store.default_uint32.get(), 0x8abcdef0U); } TEST(Types, Uint64) { stored::TestStore store; - EXPECT_EQ(store.default_uint64.get(), 0); - store.default_uint64 = 0xf123456789abcdefull; - EXPECT_EQ(store.default_uint64.get(), 0xf123456789abcdefull); + EXPECT_EQ(store.default_uint64.get(), 0ULL); + store.default_uint64 = 0xf123456789abcdefULL; + EXPECT_EQ(store.default_uint64.get(), 0xf123456789abcdefULL); } TEST(Types, Float) { stored::TestStore store; - EXPECT_EQ(store.default_float.get(), 0); - store.default_float = 3.14f; - EXPECT_FLOAT_EQ(store.default_float.get(), 3.14f); + EXPECT_EQ(store.default_float.get(), 0.F); + store.default_float = 3.14F; + EXPECT_FLOAT_EQ(store.default_float.get(), 3.14F); } TEST(Types, Double) { stored::TestStore store; - EXPECT_EQ(store.default_double.get(), 0); + EXPECT_EQ(store.default_double.get(), 0.); store.default_double = 3.14; EXPECT_DOUBLE_EQ(store.default_double.get(), 3.14); } @@ -150,7 +150,7 @@ TEST(Types, String) memset(buffer1, 0, s + 1); char* buffer2 = (char*)alloca(s + 1); memset(buffer2, 0, s + 1); - EXPECT_EQ(store.default_string.get(buffer2, s), 0); + EXPECT_EQ(store.default_string.get(buffer2, s), 0U); for(size_t i = 0; i < s + 1; i++) buffer1[i] = 'a'; @@ -166,8 +166,8 @@ TEST(Types, String) ASSERT_TRUE(s >= 4); memcpy(buffer1, "a\0b\0", 4); - EXPECT_EQ(store.default_string.set(buffer1, s), 1); - EXPECT_EQ(store.default_string.get(buffer2, s), 1); + EXPECT_EQ(store.default_string.set(buffer1, s), 1U); + EXPECT_EQ(store.default_string.get(buffer2, s), 1U); } TEST(Types, FreeVariable) @@ -179,10 +179,10 @@ TEST(Types, FreeVariable) auto v = f.apply(store); v = 10; - EXPECT_EQ(store.default_uint8.get(), 10); + EXPECT_EQ(store.default_uint8.get(), 10U); - store.default_uint8 = 11; - EXPECT_EQ(v.get(), 11); + store.default_uint8 = 11U; + EXPECT_EQ(v.get(), 11U); } } // namespace diff --git a/version/version.txt b/version/version.txt index ccd4e8c1..7ec1d6db 100644 --- a/version/version.txt +++ b/version/version.txt @@ -1 +1 @@ -2.1.0-alpha +2.1.0