diff --git a/.github/workflows/CI-Linux.yml b/.github/workflows/CI-Linux.yml new file mode 100644 index 0000000..dbb4565 --- /dev/null +++ b/.github/workflows/CI-Linux.yml @@ -0,0 +1,26 @@ +name: CI-Linux + +on: + push: + branches: + - main + - release/** + - stable + pull_request: + branches: + - main + - release/** + - codex/** + +jobs: + Tests-Linux: + runs-on: ubuntu-latest + strategy: + matrix: + std: [11, 17] + steps: + - uses: actions/checkout@v3 + - name: Build and Run Tests + run: | + sudo apt-get update && sudo apt-get install -y cmake + CXX_STANDARD=${{ matrix.std }} ./scripts/run_tests.sh diff --git a/.github/workflows/CI-Win.yml b/.github/workflows/CI-Win.yml new file mode 100644 index 0000000..3e59046 --- /dev/null +++ b/.github/workflows/CI-Win.yml @@ -0,0 +1,35 @@ +name: CI-Win + +on: + push: + branches: + - main + - release/** + - stable + pull_request: + branches: + - main + - release/** + - codex/** + +jobs: + Tests-Win: + runs-on: windows-latest + strategy: + matrix: + std: [11, 17] + steps: + - uses: actions/checkout@v3 + - uses: msys2/setup-msys2@v2 + with: + update: true + msystem: MINGW64 + install: >- + mingw-w64-x86_64-toolchain + mingw-w64-x86_64-cmake + mingw-w64-x86_64-make + - name: Build and Run Tests + shell: msys2 {0} + run: | + export CXX_STANDARD=${{ matrix.std }} + ./scripts/run_tests.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 0477fca..99c1709 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,10 +1,13 @@ cmake_minimum_required(VERSION 3.5) project(hmac_cpp LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 11) +if(NOT DEFINED CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 11) +endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) option(BUILD_EXAMPLE "Build the example program" ON) +option(BUILD_TESTS "Build the test suite" OFF) set(HMAC_SOURCES sha1.cpp @@ -24,4 +27,18 @@ if(BUILD_EXAMPLE) endif() install(TARGETS hmac DESTINATION lib) -install(FILES ${HMAC_HEADERS} DESTINATION include/hmac_cpp) \ No newline at end of file +install(FILES ${HMAC_HEADERS} DESTINATION include/hmac_cpp) + +if(BUILD_TESTS) + enable_testing() + include(FetchContent) + FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/heads/main.zip + ) + FetchContent_MakeAvailable(googletest) + add_executable(test_all test_all.cpp) + target_link_libraries(test_all PRIVATE hmac gtest_main) + target_include_directories(test_all PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) + add_test(NAME test_all COMMAND test_all) +endif() diff --git a/scripts/run_tests.sh b/scripts/run_tests.sh new file mode 100755 index 0000000..5d4facf --- /dev/null +++ b/scripts/run_tests.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +set -e +cmake -S . -B build -DBUILD_TESTS=ON -DCMAKE_CXX_STANDARD=${CXX_STANDARD:-11} +cmake --build build +cd build +ctest --output-on-failure diff --git a/sha512.cpp b/sha512.cpp index 2acb234..83d784b 100644 --- a/sha512.cpp +++ b/sha512.cpp @@ -198,15 +198,15 @@ namespace hmac_hash { void SHA512::finish(uint8_t *digest) { size_t block_nb; size_t pm_len; - size_t len_b; + uint64_t len_b; // message length in bits size_t i; - block_nb = (1 + ((SHA384_512_BLOCK_SIZE - 17) + block_nb = (1 + ((SHA384_512_BLOCK_SIZE - 9) < (m_len % SHA384_512_BLOCK_SIZE))); len_b = (m_tot_len + m_len) << 3; pm_len = block_nb << 7; memset(m_block + m_len, 0, pm_len - m_len); m_block[m_len] = 0x80; - SHA2_UNPACK32(len_b, m_block + pm_len - 4); + SHA2_UNPACK64(len_b, m_block + pm_len - 8); transform(m_block, block_nb); for(i = 0 ; i < 8; ++i) { SHA2_UNPACK64(m_h[i], &digest[i << 3]); diff --git a/test_all.cpp b/test_all.cpp new file mode 100644 index 0000000..f257de8 --- /dev/null +++ b/test_all.cpp @@ -0,0 +1,64 @@ +#include +#include +#include "hmac.hpp" +#include "hmac_utils.hpp" + +TEST(HashTest, SHA1) { + EXPECT_EQ(hmac_hash::sha1("grape"), + "bc8a2f8cdedb005b5c787692853709b060db75ff"); +} + +TEST(HashTest, SHA256) { + EXPECT_EQ(hmac_hash::sha256("grape"), + "0f78fcc486f5315418fbf095e71c0675ee07d318e5ac4d150050cd8e57966496"); +} + +TEST(HashTest, SHA512) { + EXPECT_EQ(hmac_hash::sha512("grape"), + "9375d1abdb644a01955bccad12e2f5c2bd8a3e226187e548d99c559a99461453b980123746753d07c169c22a5d9cc75cb158f0e8d8c0e713559775b5e1391fc4"); +} + +TEST(UtilsTest, ToHex) { + EXPECT_EQ(hmac::to_hex("012345"), "303132333435"); +} + +TEST(HMACTest, SHA256) { + const std::string key = "12345"; + const std::string input = "grape"; + EXPECT_EQ(hmac::get_hmac(key, input, hmac::TypeHash::SHA256, true), + "7632ac2e8ddedaf4b3e7ab195fefd17571c37c970e02e169195a158ef59e53ca"); +} + +TEST(HMACTest, SHA512) { + const std::string key = "12345"; + const std::string input = "grape"; + EXPECT_EQ(hmac::get_hmac(key, input, hmac::TypeHash::SHA512, true), + "c54ddf9647a949d0df925a1c1f8ba1c9d721a671c396fde1062a71f9f7ffae5dc10f6be15be63bb0363d051365e23f890368c54828497b9aef2eb2fc65b633e6"); +} + +TEST(HMACTest, SHA512Uppercase) { + const std::string key = "12345"; + const std::string input = "grape"; + EXPECT_EQ(hmac::get_hmac(key, input, hmac::TypeHash::SHA512, true, true), + "C54DDF9647A949D0DF925A1C1F8BA1C9D721A671C396FDE1062A71F9F7FFAE5DC10F6BE15BE63BB0363D051365E23F890368C54828497B9AEF2EB2FC65B633E6"); +} + +TEST(TOTPTest, AtTime) { + const std::string totp_key = "12345678901234567890"; + uint64_t test_time = 1234567890; + int code = hmac::get_totp_code_at(totp_key, test_time, 30, 8, hmac::TypeHash::SHA1); + EXPECT_EQ(code, 89005924); +} + +TEST(TokenTest, InvalidInterval) { + const std::string key = "12345"; + std::string token = hmac::generate_time_token(key, 60); + EXPECT_THROW(hmac::generate_time_token(key, 0), std::invalid_argument); + EXPECT_THROW(hmac::is_token_valid(token, key, 0), std::invalid_argument); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} +