diff --git a/.github/workflows/compile_project.yml b/.github/workflows/compile_project.yml index d8790aef..9e4d6215 100644 --- a/.github/workflows/compile_project.yml +++ b/.github/workflows/compile_project.yml @@ -3,8 +3,6 @@ name: Compile Project (Push/PR/Release) on: push: branches: [ development-tip, stable ] - pull_request: - branches: [ development-tip, stable ] release: types: [ created ] diff --git a/.github/workflows/execute_tests.yml b/.github/workflows/execute_tests.yml new file mode 100644 index 00000000..a1e4e3eb --- /dev/null +++ b/.github/workflows/execute_tests.yml @@ -0,0 +1,66 @@ +name: Run Unit-Tests (PR) + +on: + pull_request: + branches: [ development-tip, stable ] + +jobs: + build_makefile: + name: ${{ matrix.dist }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + dist: [ubuntu_x86_64, macos_x86_64, macos_arm64] + prog: [libtoolchain_test] + include: + - dist: ubuntu_x86_64 + os: ubuntu-latest + arch: x86_64 + bin_ext: + - dist: macos_x86_64 + os: macos-latest + arch: x86_64 + bin_ext: + - dist: macos_arm64 + os: macos-latest + arch: arm64 + bin_ext: + steps: + - uses: actions/checkout@v4 + - name: Clone submodules + run: git submodule init && git submodule update + - name: Compile ${{ matrix.prog }} + run: make PROJECT_PLATFORM_ARCH=${{ matrix.arch }} deps all + - name: Run unit-tests ${{ matrix.prog }} + run: ./bin/${{ matrix.prog }}${{ matrix.bin_ext }} --exres PASS --slow + build_visualstudio: + name: ${{ matrix.dist }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + dist: [win_x64, win_x86] + vs_sln: [libtoolchain] + bin_name: [libtoolchain-test] + include: + - dist: win_x64 + os: windows-latest + platform: x64 + configuration: Release + build_path: x64\Release + bin_ext: .exe + - dist: win_x86 + os: windows-latest + platform: x86 + configuration: Release + build_path: Release + bin_ext: .exe + steps: + - uses: actions/checkout@v4 + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v1.3 + - name: Clone submodules + run: git submodule init && git submodule update + - name: Compile ${{ matrix.vs_sln }} + run: msbuild .\build\visualstudio\${{ matrix.vs_sln }}.sln /p:configuration=${{ matrix.configuration }} /p:platform=${{ matrix.platform }} + - name: Run unit-tests ${{ matrix.bin_name }} + run: .\build\visualstudio\${{ matrix.build_path }}\${{ matrix.bin_name }}${{ matrix.bin_ext }} --exres PASS diff --git a/build/visualstudio/libtoolchain-test/libtoolchain-test.vcxproj b/build/visualstudio/libtoolchain-test/libtoolchain-test.vcxproj index 609c1327..b5114fcb 100644 --- a/build/visualstudio/libtoolchain-test/libtoolchain-test.vcxproj +++ b/build/visualstudio/libtoolchain-test/libtoolchain-test.vcxproj @@ -169,6 +169,7 @@ + @@ -285,6 +286,7 @@ + diff --git a/build/visualstudio/libtoolchain-test/libtoolchain-test.vcxproj.filters b/build/visualstudio/libtoolchain-test/libtoolchain-test.vcxproj.filters index d108efee..438b4ddd 100644 --- a/build/visualstudio/libtoolchain-test/libtoolchain-test.vcxproj.filters +++ b/build/visualstudio/libtoolchain-test/libtoolchain-test.vcxproj.filters @@ -147,6 +147,9 @@ Source Files + + Source Files + Source Files @@ -470,7 +473,6 @@ Header Files - Header Files @@ -486,13 +488,15 @@ Header Files - Header Files Header Files + + Header Files + Header Files diff --git a/build/visualstudio/libtoolchain/libtoolchain.vcxproj b/build/visualstudio/libtoolchain/libtoolchain.vcxproj index 825e2bab..f89a564b 100644 --- a/build/visualstudio/libtoolchain/libtoolchain.vcxproj +++ b/build/visualstudio/libtoolchain/libtoolchain.vcxproj @@ -179,6 +179,11 @@ + + + + + @@ -223,6 +228,8 @@ + + @@ -242,11 +249,6 @@ - - - - - @@ -320,6 +322,10 @@ + + + + @@ -350,6 +356,8 @@ + + diff --git a/build/visualstudio/libtoolchain/libtoolchain.vcxproj.filters b/build/visualstudio/libtoolchain/libtoolchain.vcxproj.filters index a22c5a25..98f76ff6 100644 --- a/build/visualstudio/libtoolchain/libtoolchain.vcxproj.filters +++ b/build/visualstudio/libtoolchain/libtoolchain.vcxproj.filters @@ -82,6 +82,18 @@ Source Files\crypto + + Source Files\crypto + + + Source Files\crypto + + + Source Files\crypto + + + Source Files\crypto + Source Files\crypto @@ -172,6 +184,12 @@ Source Files\crypto\detail + + Source Files\crypto\detail + + + Source Files\crypto\detail + Source Files\crypto\detail @@ -432,6 +450,21 @@ Header Files\tc\crypto + + Header Files\tc\crypto + + + Header Files\tc\crypto + + + Header Files\tc\crypto + + + Header Files\tc\crypto + + + Header Files\tc\crypto + Header Files\tc\crypto @@ -564,6 +597,12 @@ Header Files\tc\crypto\detail + + Header Files\tc\crypto\detail + + + Header Files\tc\crypto\detail + Header Files\tc\crypto\detail diff --git a/include/tc/crypto/EccCurveType.h b/include/tc/crypto/EccCurveType.h new file mode 100644 index 00000000..943f08b5 --- /dev/null +++ b/include/tc/crypto/EccCurveType.h @@ -0,0 +1,33 @@ + /** + * @file EccCurveType.h + * @brief Declaration of tc::crypto::EccCurveType + * @author Jack (jakcron) + * @version 0.1 + * @date 2025/06/01 + **/ +#pragma once + +namespace tc { namespace crypto { + + /** + * @struct EccCurveType + * @brief Defines supported ECC curves + */ +enum EccCurveType +{ + ECC_CURVE_TYPE_SECP192R1, /**< The 192-bit curve defined by FIPS 186-4 and SEC1. */ + ECC_CURVE_TYPE_SECP224R1, /**< The 224-bit curve defined by FIPS 186-4 and SEC1. */ + ECC_CURVE_TYPE_SECP256R1, /**< The 256-bit curve defined by FIPS 186-4 and SEC1. */ + ECC_CURVE_TYPE_SECP384R1, /**< The 384-bit curve defined by FIPS 186-4 and SEC1. */ + ECC_CURVE_TYPE_SECP521R1, /**< The 521-bit curve defined by FIPS 186-4 and SEC1. */ + ECC_CURVE_TYPE_BP256R1, /**< 256-bit Brainpool curve. */ + ECC_CURVE_TYPE_BP384R1, /**< 384-bit Brainpool curve. */ + ECC_CURVE_TYPE_BP512R1, /**< 512-bit Brainpool curve. */ + ECC_CURVE_TYPE_CURVE25519, /**< Curve25519. */ + ECC_CURVE_TYPE_SECP192K1, /**< 192-bit "Koblitz" curve. */ + ECC_CURVE_TYPE_SECP224K1, /**< 224-bit "Koblitz" curve. */ + ECC_CURVE_TYPE_SECP256K1, /**< 256-bit "Koblitz" curve. */ + ECC_CURVE_TYPE_CURVE448, /**< Curve448. */ +}; + +}} // namespace tc::crypto \ No newline at end of file diff --git a/include/tc/crypto/EccKey.h b/include/tc/crypto/EccKey.h new file mode 100644 index 00000000..2b08c444 --- /dev/null +++ b/include/tc/crypto/EccKey.h @@ -0,0 +1,95 @@ + /** + * @file EccKey.h + * @brief Declarations for structures to store ECC keys. + * @author Jack (jakcron) + * @version 0.1 + * @date 2025/06/01 + **/ +#pragma once +#include +#include +#include + +#include + +namespace tc { namespace crypto { + + + /** + * @struct EccKey + * @brief Struct for storing a ECC key. For use with ECC operations. + * + * @note The public/private components are not compressed. + */ +struct EccKey +{ + EccCurveType curve_type; + tc::ByteData d; /**< Private component - big endian d integer */ + tc::ByteData Q; /**< Public component - big endian Q point */ +}; + + /** + * @struct EccPublicKey + * @brief This extends EccKey, exposing a constructor to create an ECC public key. + */ +struct EccPublicKey : public EccKey +{ + /** + * @brief This constructs a @ref EccKey from the public component. + * + * @param[in] curve_type ECC Curve Type. + * @param[in] Q Buffer containing big-endian Q point. + * @param[in] Q_size Size in bytes of Q. + * + * @pre @p ec_type must be of type @ref EccCurveType + * @pre @p Q != nullptr + * @pre @p Q_size != 0 + */ + EccPublicKey(EccCurveType curve_type, const byte_t* Q, size_t Q_size); +}; + + /** + * @struct EccPrivateKey + * @brief This extends EccKey, exposing a constructor to create an ECC private key from a modulus and private exponent. + */ +struct EccPrivateKey : public EccKey +{ + /** + * @brief This constructs a @ref EccKey from the private and public components. + * + * @param[in] curve_type ECC Curve Type + * @param[in] d Buffer containing big-endian d integer. + * @param[in] d_size Size in bytes of d. + * @param[in] Q Buffer containing big-endian Q point. + * @param[in] Q_size Size in bytes of Q. + * + * @pre @p ec_type must be of type @ref EccCurveType + * @pre @p d != nullptr + * @pre @p d_size != 0 + * @pre @p Q != nullptr + * @pre @p Q_size != 0 + */ + EccPrivateKey(EccCurveType curve_type, const byte_t* d, size_t d_size, const byte_t* Q, size_t Q_size); + + /** + * @brief This constructs a @ref EccKey from the private component (and generate the public component). + * + * @param[in] curve_type ECC Curve Type + * @param[in] d Buffer containing big-endian d integer. + * @param[in] d_size Size in bytes of d. + * + * @pre @p ec_type must be of type @ref EccCurveType + * @pre @p d != nullptr + * @pre @p d_size != 0 + */ + EccPrivateKey(EccCurveType curve_type, const byte_t* d, size_t d_size); + + /** + * @brief Generate public key from this private key. + * + * @return EccKey containing the public key. + */ + EccKey getPublicKey(); +}; + +}} // namespace tc::crypto \ No newline at end of file diff --git a/include/tc/crypto/EccKeyGenerator.h b/include/tc/crypto/EccKeyGenerator.h new file mode 100644 index 00000000..97af7e01 --- /dev/null +++ b/include/tc/crypto/EccKeyGenerator.h @@ -0,0 +1,114 @@ + /** + * @file EccKeyGenerator.h + * @brief Declarations for API resources for generating ECC keys. + * @author Jack (jakcron) + * @version 0.1 + * @date 2025/06/01 + **/ +#pragma once +#include +#include +#include +#include + +namespace tc { namespace crypto { + + /** + * @class EccKeyGenerator + * @brief Class for generating ECC keys. + * + * @details + * The underlying PRNG algorithm is CTR_DBRG. + */ +class EccKeyGenerator +{ +public: + /** + * @brief Default constructor. + */ + EccKeyGenerator() : + mImpl() + {} + + /** + * @brief Generate an ECC key. + * + * @param[out] key Reference to generated ECC key. + * @param[in] curve_type ECC Curve Type. + * + * @pre + * - @p ec_type must be of type @ref EccCurveType + * + * @post + * - The generated key is written to key. + * + * @throw tc::crypto::ArgumentException @p curve_type was not supported/valid. + */ + void generateKey(EccKey& key, EccCurveType curve_type) + { + size_t ecc_int_byte_length = EccUtil::eccIntegerByteLength(curve_type); + + if (ecc_int_byte_length == 0) throw tc::ArgumentException("tc::crypto::EccKeyGenerator::generateKey()", "curve_type was not supported/valid"); + + key.curve_type = curve_type; + key.d = tc::ByteData(ecc_int_byte_length); + key.Q = tc::ByteData(ecc_int_byte_length * 2); + + mImpl.generateKey(curve_type, key.d.data(), key.d.size(), key.Q.data(), key.Q.size()); + } + + /** + * @brief Generate an ECC public key from an ECC private key key. + * + * @param[out] public_key Object to store generated public key. + * @param[in] private_key Private key to generate public key from. + * + * @post + * - The generated public key is written to public_key. + * + * @throw tc::crypto::ArgumentException @p private_key curve_type was not supported/valid. + */ + void generatePublicKey(EccKey& public_key, const EccKey& private_key) + { + size_t ecc_int_byte_length = EccUtil::eccIntegerByteLength(private_key.curve_type); + + if (ecc_int_byte_length == 0) throw tc::ArgumentException("tc::crypto::EccKeyGenerator::generatePublicKey()", "curve_type was not supported/valid"); + + public_key.curve_type = private_key.curve_type; + public_key.d = tc::ByteData(0); + public_key.Q = tc::ByteData(ecc_int_byte_length * 2); + + mImpl.generatePublicKey(public_key.curve_type, private_key.d.data(), private_key.d.size(), public_key.Q.data(), public_key.Q.size()); + } + +private: + detail::EccKeyGeneratorImpl mImpl; +}; + + /** + * @brief Utility function for generating an ECC key. + * + * @param[out] key Object to store generated ECC key. + * @param[in] curve_type Type of ECC curve to generate + * + * @post + * - The generated key is written to key. + * + * @throw tc::crypto::ArgumentException @p curve_type was not supported/valid. + */ +void GenerateEccKey(EccKey& key, EccCurveType curve_type); + + /** + * @brief Utility function for generating an ECC public key from an ECC private key. + * + * @param[out] public_key Object to store generated public key. + * @param[in] private_key Private key to generate public key from. + * + * @post + * - The generated public key is written to public_key. + * + * @throw tc::crypto::ArgumentException @p curve_type was not supported/valid. + */ +void GenerateEccPublicKey(EccKey& public_key, const EccKey& private_key); + +}} // namespace tc::crypto \ No newline at end of file diff --git a/include/tc/crypto/EccUtil.h b/include/tc/crypto/EccUtil.h new file mode 100644 index 00000000..3c2dd39d --- /dev/null +++ b/include/tc/crypto/EccUtil.h @@ -0,0 +1,32 @@ + /** + * @file EccUtil.h + * @brief Declaration of tc::crypto::EccUtil + * @author Jack (jakcron) + * @version 0.1 + * @date 2025/06/08 + **/ +#pragma once +#include +#include + +namespace tc { namespace crypto { + + /** + * @class EccUtil + * @brief Utility functions for ECC operations based classes. + **/ +class EccUtil +{ +public: + /** + * @brief Get length of ECC integer in bits. + **/ + static size_t eccIntegerBitLength(EccCurveType curve_type); + + /** + * @brief Get length of ECC integer in bytes (rounding up). + **/ + static size_t eccIntegerByteLength(EccCurveType curve_type); +}; + +}} // namespace tc::crypto \ No newline at end of file diff --git a/include/tc/crypto/EcdhSharedSecretGenerator.h b/include/tc/crypto/EcdhSharedSecretGenerator.h new file mode 100644 index 00000000..7722076f --- /dev/null +++ b/include/tc/crypto/EcdhSharedSecretGenerator.h @@ -0,0 +1,133 @@ + /** + * @file EcdhSharedSecretGenerator.h + * @brief Declarations for API resources for using ECC keys to generate shared secrets using ECDH. + * @author Jack (jakcron) + * @version 0.1 + * @date 2025/08/06 + **/ +#pragma once +#include +#include +#include +#include + +#include + +namespace tc { namespace crypto { + + /** + * @class EcdhSharedSecretGenerator + * @brief Class for using ECC keys to generate shared secrets using ECDH. + */ +class EcdhSharedSecretGenerator +{ +public: + /** + * @brief Default constructor. + */ + EcdhSharedSecretGenerator() : + mState(State_NotInitialized), + mImpl() + {} + + /** + * @brief Utility function for generating an ECDH shared secret. + + * @param[in] private_key ECC private key of the current party. + * @param[in] public_key ECC public key of other party. + * + * @throw tc::crypto::ArgumentException @p private_key was not a valid private key. + * @throw tc::crypto::ArgumentException @p public_key was not a valid public key. + * @throw tc::crypto::ArgumentException @p private_key or @p public_key curve type is not supported/valid. + * @throw tc::crypto::ArgumentException @p private_key and @p public_key did not have the same curve type. + */ + void initialize(const EccKey& private_key, const EccKey& public_key) + { + if (private_key.curve_type != public_key.curve_type) + { + throw tc::ArgumentOutOfRangeException("private_key and public_key did not have the same curve type."); + } + + size_t ecc_integer_len = EccUtil::eccIntegerByteLength(private_key.curve_type); + + if (ecc_integer_len == 0) + { + throw tc::ArgumentOutOfRangeException("private_key or public_key curve type is not supported/valid."); + } + + if (private_key.d.size() != ecc_integer_len) + { + throw tc::ArgumentOutOfRangeException("private_key was not a valid private key."); + } + + if (public_key.Q.size() != (ecc_integer_len * 2)) + { + throw tc::ArgumentOutOfRangeException("public_key was not a valid public key."); + } + + mPrivateKey = private_key; + mPublicKey = public_key; + + mState = State_Initialized; + } + + /** + * @brief Utility function for generating an ECDH shared secret. + * + * @param[out] data Buffer to hold shared secret data. + * @param[in] data_size Size of @p data buffer. + * + * @post + * - The generated shared secret is written to data up to data_size bytes. + * - Shared secret length cannot exceed the bitsize of the curve. + * + * @throw tc::crypto::ArgumentNullException @p data_size > 0 but @p data was not nullptr. + */ + void generateSharedSecretBytes(byte_t* data, size_t data_size) + { + if (mState != State_Initialized) + return; + + // todo input sani logic here + if (data_size > 0 && data != nullptr) + { + throw tc::ArgumentNullException("data_size > 0 but data was not nullptr."); + } + + mImpl.generateSharedSecret(mPrivateKey.curve_type, data, data_size, mPrivateKey.d.data(), mPrivateKey.d.size(), mPublicKey.Q.data(), mPublicKey.Q.size()); + } + +private: + enum State + { + State_NotInitialized, + State_Initialized + }; + + State mState; + tc::crypto::EccKey mPrivateKey; + tc::crypto::EccKey mPublicKey; + detail::EcdhSharedSecretGeneratorImpl mImpl; +}; + + /** + * @brief Utility function for generating an ECDH shared secret. + * + * @param[out] data Buffer to hold shared secret data. + * @param[in] data_size Size of @p data buffer. + * @param[in] private_key ECC private key of the current party. + * @param[in] public_key ECC public key of other party. + * + * @post + * - The generated shared secret is written to data up to data_size bytes. + * - Shared secret length cannot exceed the bitsize of the curve. + * + * @throw tc::crypto::ArgumentNullException @p data_size > 0 but @p data was not nullptr. + * @throw tc::crypto::ArgumentException @p private_key was not a valid private key. + * @throw tc::crypto::ArgumentException @p public_key was not a valid public key. + * @throw tc::crypto::ArgumentException @p private_key or @p public_key curve type is not supported/valid. + * @throw tc::crypto::ArgumentException @p private_key and @p public_key did not have the same curve type. + */ +void GenerateEcdhSharedSecret(byte_t* data, size_t data_size, const EccKey& private_key, const EccKey& public_key); + +}} // namespace tc::crypto \ No newline at end of file diff --git a/include/tc/crypto/detail/BlockUtilImpl.h b/include/tc/crypto/detail/BlockUtilImpl.h index 791a2d4f..1774cd1d 100644 --- a/include/tc/crypto/detail/BlockUtilImpl.h +++ b/include/tc/crypto/detail/BlockUtilImpl.h @@ -29,6 +29,30 @@ inline void incr_counter(byte_t* counter, uint64_t incr) } } +template <> +inline void incr_counter<8>(byte_t* counter, uint64_t incr) +{ + tc::bn::be64* counter_words = (tc::bn::be64*)counter; + + uint64_t carry = incr; + while (carry > 0) + { + uint64_t word = counter_words[0].unwrap(); + uint64_t remaining = std::numeric_limits::max() - word; + + if (remaining > carry) + { + counter_words[0].wrap(word + carry); + carry = 0; + } + else + { + counter_words[0].wrap(carry - remaining - 1); + carry = 1; + } + } +} + template <> inline void incr_counter<16>(byte_t* counter, uint64_t incr) { @@ -59,6 +83,12 @@ inline void xor_block(byte_t* dst, const byte_t* src_a, const byte_t* src_b) for (size_t i = 0; i < BlockSize; i++) { dst[i] = src_a[i] ^ src_b[i];} } +template <> +inline void xor_block<8>(byte_t* dst, const byte_t* src_a, const byte_t* src_b) +{ + ((uint64_t*)dst)[0] = ((uint64_t*)src_a)[0] ^ ((uint64_t*)src_b)[0]; +} + template <> inline void xor_block<16>(byte_t* dst, const byte_t* src_a, const byte_t* src_b) { diff --git a/include/tc/crypto/detail/EccKeyGeneratorImpl.h b/include/tc/crypto/detail/EccKeyGeneratorImpl.h new file mode 100644 index 00000000..c2b686c5 --- /dev/null +++ b/include/tc/crypto/detail/EccKeyGeneratorImpl.h @@ -0,0 +1,96 @@ + /** + * @file EccKeyGeneratorImpl.h + * @brief Declaration of tc::crypto::detail::EccKeyGeneratorImpl + * @author Jack (jakcron) + * @version 0.1 + * @date 2025/05/24 + **/ +#pragma once +#include + +#include +#include +#include +#include + +namespace tc { namespace crypto { namespace detail { + + /** + * @class EccKeyGeneratorImpl + * @brief This class implements the ECC key generation. + */ +class EccKeyGeneratorImpl +{ +public: + /** + * @brief Default constructor + * @details + * This initializes ECC key generator state. + */ + EccKeyGeneratorImpl(); + + /** + * @brief Destructor + * @details + * Cleans up ECC key generator state. + */ + ~EccKeyGeneratorImpl(); + + /** + * @brief Generate an ECC key. + * + * @param[in] ec_type Type of Elliptic Curve @ref EccCurveType + * @param[out] d Buffer to store private component. + * @param[in] d_size Size of private component buffer. + * @param[out] Q Buffer to store public component. + * @param[in] Q_size Size of public component buffer. + * + * @pre + * - @p ec_type must be of type @ref EccCurveType + * @post + * - Key components are exported if the related buffers were not null. + * + * @note + * - Key components can be optionally not exported if the corresponding input variables are null and zero. + * + * @throw tc::ArgumentOutOfRangeException @p ec_type was not of type @ref EccCurveType + * @throw tc::crypto::CryptoException An unexpected error has occurred. + * @throw tc::crypto::CryptoException Something failed during generation of a key. + * @throw tc::crypto::CryptoException The random generator failed to generate non-zeros. + * @throw tc::ArgumentException @p d was not null, but @p d_size was not large enough. + * @throw tc::ArgumentException @p Q was not null, but @p Q_size was not large enough. + */ + void generateKey(EccCurveType ec_type, byte_t* d, size_t d_size, byte_t* Q, size_t Q_size); + + /** + * @brief Generate an ECC Public key based on the private component. + * + * @param[in] ec_type Type of Elliptic Curve @ref EccCurveType + * @param[in] d Buffer to store private component. + * @param[in] d_size Size of private component buffer. + * @param[out] Q Buffer to store public component. + * @param[in] Q_size Size of public component buffer. + * + * @pre + * - @p ec_type must be of type @ref EccCurveType + * @post + * - Key components are exported if the related buffers were not null. + * + * @note + * - Key components can be optionally not exported if the corresponding input variables are null and zero. + * + * @throw tc::ArgumentOutOfRangeException @p ec_type was not of type @ref EccCurveType + * @throw tc::crypto::CryptoException An unexpected error has occurred. + * @throw tc::crypto::CryptoException Something failed during generation of a key. + * @throw tc::ArgumentException @p d was null, or @p d_size was not large enough. + * @throw tc::ArgumentException @p Q was not null, but @p Q_size was not large enough. + */ + void generatePublicKey(EccCurveType ec_type, byte_t* d, size_t d_size, byte_t* Q, size_t Q_size); +private: + static const std::string kClassName; + + struct ImplCtx; + std::unique_ptr mImplCtx; +}; + +}}} // namespace tc::crypto::detail \ No newline at end of file diff --git a/include/tc/crypto/detail/EcdhSharedSecretGeneratorImpl.h b/include/tc/crypto/detail/EcdhSharedSecretGeneratorImpl.h new file mode 100644 index 00000000..9bc49b19 --- /dev/null +++ b/include/tc/crypto/detail/EcdhSharedSecretGeneratorImpl.h @@ -0,0 +1,74 @@ + /** + * @file EcdhSharedSecretGeneratorImpl.h + * @brief Declaration of tc::crypto::detail::EcdhSharedSecretGeneratorImpl + * @author Jack (jakcron) + * @version 0.1 + * @date 2025/08/04 + **/ +#pragma once +#include + +#include +#include +#include +#include + +namespace tc { namespace crypto { namespace detail { + + /** + * @class EcdhSharedSecretGeneratorImpl + * @brief This class implements the ECDH shared secret generation. + */ +class EcdhSharedSecretGeneratorImpl +{ +public: + /** + * @brief Default constructor + * @details + * This initializes ECDH shared secret generator state. + */ + EcdhSharedSecretGeneratorImpl(); + + /** + * @brief Destructor + * @details + * Cleans up ECDH shared secret generator state. + */ + ~EcdhSharedSecretGeneratorImpl(); + + /** + * @brief Generate an ECDH shared secret based on own prvate key, external public key. + * + * @param[in] ec_type Type of Elliptic Curve @ref EccCurveType + * @param[out] z Buffer to store shared secret. + * @param[in] z_size Size of shared secret buffer. + * @param[in] d Buffer to store private component. + * @param[in] d_size Size of private component buffer. + * @param[in] Q Buffer to store public component. + * @param[in] Q_size Size of public component buffer. + * + * @pre + * - @p ec_type must be of type @ref EccCurveType + * - @p d & @p Q must be from the same curve. + * @post + * - @p z will be populated with generated shared secret. + * + * @note + * - Secret can be optionally not generated if the corresponding input variables are null and zero. + * + * @throw tc::ArgumentOutOfRangeException @p ec_type was not of type @ref EccCurveType + * @throw tc::crypto::CryptoException An unexpected error has occurred. + * @throw tc::crypto::CryptoException Something failed during generation of the shared secret. + * @throw tc::ArgumentException @p z was not null, but @p z_size was not large enough. + * @throw tc::ArgumentException @p d was not null, but @p d_size was not large enough. + * @throw tc::ArgumentException @p Q was not null, but @p Q_size was not large enough. + */ + void generateSharedSecret(EccCurveType ec_type, byte_t* z, size_t z_size, const byte_t* d, size_t d_size, const byte_t* Q, size_t Q_size); +private: + static const std::string kClassName; + + struct ImplCtx; + std::unique_ptr mImplCtx; +}; + +}}} // namespace tc::crypto::detail \ No newline at end of file diff --git a/include/tc/os/Environment.h b/include/tc/os/Environment.h index faa5e33a..55cdd993 100644 --- a/include/tc/os/Environment.h +++ b/include/tc/os/Environment.h @@ -2,12 +2,15 @@ * @file Environment.h * @brief Declarations for API resources for accessing run-time environment * @author Jack (jakcron) - * @version 0.1 - * @date 2020/06/12 + * @version 0.2 + * @date 2025/08/05 **/ #pragma once #include #include +#include + +#include namespace tc { namespace os { @@ -25,4 +28,17 @@ namespace tc { namespace os { */ bool getEnvVar(const std::string& name, std::string& value); + /** + * @brief Get OS defined temporary directory path. + * + * @details This function returns the temporary directory path, that can be used for storing files during run-time of the application. + * + * @param[out] dir_path Refence to path object to populate with temporary directory path. + * + * @post @p dir_path will contain the temporary directory path. + * + * @throws tc::io::DirectoryNotFoundException Temporary directory could not be determined. + */ +void getTempDirPath(tc::io::Path& dir_path); + }} // namespace tc::cli \ No newline at end of file diff --git a/src/crypto/EccKey.cpp b/src/crypto/EccKey.cpp new file mode 100644 index 00000000..268a112a --- /dev/null +++ b/src/crypto/EccKey.cpp @@ -0,0 +1,58 @@ +#include +#include +#include + +tc::crypto::EccPublicKey::EccPublicKey(EccCurveType curve_type, const byte_t* Q, size_t Q_size) +{ + if (EccUtil::eccIntegerByteLength(curve_type) == 0) throw tc::ArgumentException("tc::crypto::EccPublicKey()", "curve_type was not supported/valid"); + + if (Q != nullptr && Q_size != 0) + { + this->curve_type = curve_type; + this->d = tc::ByteData(); + this->Q = tc::ByteData(Q, Q_size); + } +} + +tc::crypto::EccPrivateKey::EccPrivateKey(EccCurveType curve_type, const byte_t* d, size_t d_size, const byte_t* Q, size_t Q_size) +{ + if (EccUtil::eccIntegerByteLength(curve_type) == 0) throw tc::ArgumentException("tc::crypto::EccPublicKey()", "curve_type was not supported/valid"); + + if (d != nullptr && d_size != 0 && Q != nullptr && Q_size != 0) + { + this->curve_type = curve_type; + this->d = tc::ByteData(d, d_size); + this->Q = tc::ByteData(Q, Q_size); + } +} + +tc::crypto::EccPrivateKey::EccPrivateKey(EccCurveType curve_type, const byte_t* d, size_t d_size) +{ + size_t ecc_int_byte_length = EccUtil::eccIntegerByteLength(curve_type); + if (ecc_int_byte_length == 0) throw tc::ArgumentException("tc::crypto::EccPublicKey()", "curve_type was not supported/valid"); + + if (d != nullptr && d_size != 0) + { + this->curve_type = curve_type; + this->d = tc::ByteData(d, d_size); + + tc::crypto::EccKey pub_key; + GenerateEccPublicKey(pub_key, *this); + + this->Q = pub_key.Q; + } +} + +tc::crypto::EccKey tc::crypto::EccPrivateKey::getPublicKey() +{ + // generate public component if not present + if (this->Q.data() == nullptr || this->Q.size() == 0) + { + tc::crypto::EccKey pub_key; + GenerateEccPublicKey(pub_key, *this); + + this->Q = pub_key.Q; + } + + return EccPublicKey(this->curve_type, this->Q.data(), this->Q.size()); +} \ No newline at end of file diff --git a/src/crypto/EccKeyGenerator.cpp b/src/crypto/EccKeyGenerator.cpp new file mode 100644 index 00000000..a8642827 --- /dev/null +++ b/src/crypto/EccKeyGenerator.cpp @@ -0,0 +1,13 @@ +#include + +void tc::crypto::GenerateEccKey(EccKey& key, EccCurveType curve_type) +{ + tc::crypto::EccKeyGenerator impl; + impl.generateKey(key, curve_type); +} + +void tc::crypto::GenerateEccPublicKey(EccKey& public_key, const EccKey& private_key) +{ + tc::crypto::EccKeyGenerator impl; + impl.generatePublicKey(public_key, private_key); +} \ No newline at end of file diff --git a/src/crypto/EccUtil.cpp b/src/crypto/EccUtil.cpp new file mode 100644 index 00000000..b090bc49 --- /dev/null +++ b/src/crypto/EccUtil.cpp @@ -0,0 +1,59 @@ +#include + +size_t tc::crypto::EccUtil::eccIntegerBitLength(EccCurveType curve_type) +{ + size_t ecc_int_bit_length = 0; + switch (curve_type) + { + case (ECC_CURVE_TYPE_SECP192R1): + ecc_int_bit_length = 192; + break; + case (ECC_CURVE_TYPE_SECP224R1): + ecc_int_bit_length = 224; + break; + case (ECC_CURVE_TYPE_SECP256R1): + ecc_int_bit_length = 256; + break; + case (ECC_CURVE_TYPE_SECP384R1): + ecc_int_bit_length = 384; + break; + case (ECC_CURVE_TYPE_SECP521R1): + ecc_int_bit_length = 521; + break; + case (ECC_CURVE_TYPE_BP256R1): + ecc_int_bit_length = 256; + break; + case (ECC_CURVE_TYPE_BP384R1): + ecc_int_bit_length = 384; + break; + case (ECC_CURVE_TYPE_BP512R1): + ecc_int_bit_length = 512; + break; + case (ECC_CURVE_TYPE_CURVE25519): + ecc_int_bit_length = 255; + break; + case (ECC_CURVE_TYPE_SECP192K1): + ecc_int_bit_length = 192; + break; + case (ECC_CURVE_TYPE_SECP224K1): + ecc_int_bit_length = 224; + break; + case (ECC_CURVE_TYPE_SECP256K1): + ecc_int_bit_length = 256; + break; + case (ECC_CURVE_TYPE_CURVE448): + ecc_int_bit_length = 448; + break; + default: + ecc_int_bit_length = 0; + break; + } + + return ecc_int_bit_length; +} + +size_t tc::crypto::EccUtil::eccIntegerByteLength(EccCurveType curve_type) +{ + size_t ecc_int_bit_length = eccIntegerBitLength(curve_type); + return ecc_int_bit_length / 8 + (ecc_int_bit_length % 8 != 0); +} diff --git a/src/crypto/EcdhSharedSecretGenerator.cpp b/src/crypto/EcdhSharedSecretGenerator.cpp new file mode 100644 index 00000000..870c1e34 --- /dev/null +++ b/src/crypto/EcdhSharedSecretGenerator.cpp @@ -0,0 +1,8 @@ +#include + +void tc::crypto::GenerateEcdhSharedSecret(byte_t* data, size_t data_size, const EccKey& private_key, const EccKey& public_key) +{ + tc::crypto::EcdhSharedSecretGenerator gen; + gen.initialize(private_key, public_key); + gen.generateSharedSecretBytes(data, data_size); +} \ No newline at end of file diff --git a/src/crypto/detail/EccKeyGeneratorImpl.cpp b/src/crypto/detail/EccKeyGeneratorImpl.cpp new file mode 100644 index 00000000..0524e05b --- /dev/null +++ b/src/crypto/detail/EccKeyGeneratorImpl.cpp @@ -0,0 +1,248 @@ +#include +#include +#include +#include +#include +#include + +const std::string tc::crypto::detail::EccKeyGeneratorImpl::kClassName = "tc::crypto::detail::EccKeyGeneratorImpl"; + +struct tc::crypto::detail::EccKeyGeneratorImpl::ImplCtx +{ + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_entropy_context entropy; +}; + +tc::crypto::detail::EccKeyGeneratorImpl::EccKeyGeneratorImpl() : + mImplCtx(new ImplCtx()) +{ + mbedtls_entropy_init( &(mImplCtx->entropy) ); + mbedtls_ctr_drbg_init( &(mImplCtx->ctr_drbg) ); + + int ret = mbedtls_ctr_drbg_seed( &(mImplCtx->ctr_drbg), mbedtls_entropy_func, &(mImplCtx->entropy), (const unsigned char *)kClassName.c_str(), kClassName.size() ); + switch (ret) + { + case (0): + break; + case (MBEDTLS_ERR_ENTROPY_SOURCE_FAILED): + throw tc::crypto::CryptoException(kClassName, "mbedtls_ctr_drbg_seed() Entropy source failed"); + default: + throw tc::crypto::CryptoException(kClassName, "mbedtls_ctr_drbg_seed() An unexpected error occurred"); + } +} + +tc::crypto::detail::EccKeyGeneratorImpl::~EccKeyGeneratorImpl() +{ + mbedtls_ctr_drbg_free( &(mImplCtx->ctr_drbg) ); + mbedtls_entropy_free( &(mImplCtx->entropy) ); +} + +static mbedtls_ecp_group_id convertToMbedtlsEcpGroupId(tc::crypto::EccCurveType ec_type) +{ + mbedtls_ecp_group_id group_id = MBEDTLS_ECP_DP_NONE; + switch (ec_type) + { + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP192R1: + group_id = MBEDTLS_ECP_DP_SECP192R1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP224R1: + group_id = MBEDTLS_ECP_DP_SECP224R1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP256R1: + group_id = MBEDTLS_ECP_DP_SECP256R1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP384R1: + group_id = MBEDTLS_ECP_DP_SECP384R1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP521R1: + group_id = MBEDTLS_ECP_DP_SECP521R1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_BP256R1: + group_id = MBEDTLS_ECP_DP_BP256R1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_BP384R1: + group_id = MBEDTLS_ECP_DP_BP384R1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_BP512R1: + group_id = MBEDTLS_ECP_DP_BP512R1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_CURVE25519: + group_id = MBEDTLS_ECP_DP_CURVE25519; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP192K1: + group_id = MBEDTLS_ECP_DP_SECP192K1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP224K1: + group_id = MBEDTLS_ECP_DP_SECP224K1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP256K1: + group_id = MBEDTLS_ECP_DP_SECP256K1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_CURVE448: + group_id = MBEDTLS_ECP_DP_CURVE448; + break; + } + + return group_id; +} + +void tc::crypto::detail::EccKeyGeneratorImpl::generateKey(EccCurveType ec_type, byte_t* d, size_t d_size, byte_t* q, size_t q_size) +{ + mbedtls_ecp_group_id group_id = convertToMbedtlsEcpGroupId(ec_type); + if (group_id == MBEDTLS_ECP_DP_NONE) + { + throw tc::ArgumentOutOfRangeException(kClassName, fmt::format("ec_type ({}) was not valid.", (uint32_t)ec_type)); + } + + mbedtls_ecp_keypair key; + mbedtls_ecp_keypair_init( &key ); + + int ret = 1; + + // generate key + ret = mbedtls_ecp_gen_key( group_id, &key, mbedtls_ctr_drbg_random, &(mImplCtx->ctr_drbg) ); + switch (ret) + { + case (0): + break; + default: + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_ecp_gen_key() An unexpected error occurred. {:x})", ret)); + + } + + // determine curve integer length for this key + size_t p_len = mbedtls_mpi_size( &key.grp.P ); + + // export private component + if (d != nullptr) + { + if (d_size < p_len) + { + throw tc::ArgumentNullException(kClassName, "d was not null, but d_size was insufficent to store private exponent"); + } + + ret = mbedtls_mpi_write_binary( &key.d, d, std::min(d_size, p_len) ); + switch (ret) + { + case (0): + break; + //case (MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL): + default: + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_mpi_write_binary() An unexpected error occurred. {:x})", ret)); + } + } + + // export public component + if (q != nullptr) + { + // when writing the public component, it seems to be standard to reserve the full integer point for X,Y of Q, regardless of how many bits are actually used + // even if Q.Y is empty (length 0) + // if key is < min size + if (q_size < (p_len * 2)) + { + throw tc::ArgumentNullException(kClassName, "q was not null, q_size was insufficent to store public exponent"); + } + + ret = mbedtls_mpi_write_binary( &key.Q.X, q, p_len ); + switch (ret) + { + case (0): + break; + //case (MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL): + default: + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_mpi_write_binary() An unexpected error occurred. {:x})", ret)); + } + + ret = mbedtls_mpi_write_binary( &key.Q.Y, q + p_len, p_len ); + switch (ret) + { + case (0): + break; + //case (MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL): + default: + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_mpi_write_binary() An unexpected error occurred. {:x})", ret)); + } + } + + // clear key from mbedtls context + mbedtls_ecp_keypair_free( &key ); +} + +void tc::crypto::detail::EccKeyGeneratorImpl::generatePublicKey(EccCurveType ec_type, byte_t* d, size_t d_size, byte_t* q, size_t q_size) +{ + mbedtls_ecp_group_id group_id = convertToMbedtlsEcpGroupId(ec_type); + if (group_id == MBEDTLS_ECP_DP_NONE) { throw tc::ArgumentOutOfRangeException(kClassName, "ec_type was not valid."); } + if (d == nullptr) { throw tc::ArgumentNullException(kClassName, "d was null"); } + + mbedtls_ecp_keypair key; + mbedtls_ecp_keypair_init( &key ); + + int ret = 1; + // load group + ret = mbedtls_ecp_group_load( &key.grp, group_id ); + switch (ret) + { + case (0): + break; + default: + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_ecp_group_load() An unexpected error occurred. {:x})", ret)); + } + + // import private component (d) + ret = mbedtls_mpi_read_binary( &key.d, d, d_size ); + switch (ret) + { + case (0): + break; + //case (MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL): + default: + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_mpi_read_binary() An unexpected error occurred.", ret)); + } + + // generate public component (Q) from private exponent (d) and base point (G) + ret = mbedtls_ecp_mul( &key.grp, &key.Q, &key.d, &key.grp.G, mbedtls_ctr_drbg_random, &(mImplCtx->ctr_drbg) ); + switch (ret) + { + case (0): + break; + case (MBEDTLS_ERR_ECP_INVALID_KEY): + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_ecp_mul() Invalid public or private key. {:x})", ret)); + default: + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_ecp_mul() An unexpected error occurred. {:x})", ret)); + } + + // export public component + if (q != nullptr) + { + size_t p_len = mbedtls_mpi_size( &key.grp.P ); + + // if key is < min size + if (q_size < (p_len * 2)) + { + throw tc::ArgumentNullException(kClassName, "q was not null, q_size was insufficent to store public exponent"); + } + + ret = mbedtls_mpi_write_binary( &key.Q.X, q, p_len ); + switch (ret) + { + case (0): + break; + //case (MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL): + default: + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_mpi_write_binary() An unexpected error occurred. {:x})", ret)); + } + + ret = mbedtls_mpi_write_binary( &key.Q.Y, q + p_len, p_len ); + switch (ret) + { + case (0): + break; + //case (MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL): + default: + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_mpi_write_binary() An unexpected error occurred. {:x})", ret)); + } + } + + // clear key from mbedtls context + mbedtls_ecp_keypair_free( &key ); +} \ No newline at end of file diff --git a/src/crypto/detail/EcdhSharedSecretGeneratorImpl.cpp b/src/crypto/detail/EcdhSharedSecretGeneratorImpl.cpp new file mode 100644 index 00000000..6799e5a0 --- /dev/null +++ b/src/crypto/detail/EcdhSharedSecretGeneratorImpl.cpp @@ -0,0 +1,226 @@ +#include +#include +#include +#include +#include +#include + +const std::string tc::crypto::detail::EcdhSharedSecretGeneratorImpl::kClassName = "tc::crypto::detail::EcdhSharedSecretGeneratorImpl"; + +struct tc::crypto::detail::EcdhSharedSecretGeneratorImpl::ImplCtx +{ + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_entropy_context entropy; + + mbedtls_ecp_group grp; // ec group + mbedtls_mpi d; // source private key + mbedtls_ecp_point Q; // source public key + mbedtls_ecp_point P; // target point +}; + +tc::crypto::detail::EcdhSharedSecretGeneratorImpl::EcdhSharedSecretGeneratorImpl() : + mImplCtx(new ImplCtx()) +{ + mbedtls_entropy_init( &(mImplCtx->entropy) ); + mbedtls_ctr_drbg_init( &(mImplCtx->ctr_drbg) ); + + int ret = mbedtls_ctr_drbg_seed( &(mImplCtx->ctr_drbg), mbedtls_entropy_func, &(mImplCtx->entropy), (const unsigned char *)kClassName.c_str(), kClassName.size() ); + switch (ret) + { + case (0): + break; + case (MBEDTLS_ERR_ENTROPY_SOURCE_FAILED): + throw tc::crypto::CryptoException(kClassName, "mbedtls_ctr_drbg_seed() Entropy source failed"); + default: + throw tc::crypto::CryptoException(kClassName, "mbedtls_ctr_drbg_seed() An unexpected error occurred"); + } + + mbedtls_ecp_group_init( &(mImplCtx->grp) ); + mbedtls_mpi_init( &(mImplCtx->d) ); + mbedtls_ecp_point_init( &(mImplCtx->Q) ); + mbedtls_ecp_point_init( &(mImplCtx->P) ); +} + +tc::crypto::detail::EcdhSharedSecretGeneratorImpl::~EcdhSharedSecretGeneratorImpl() +{ + mbedtls_ecp_point_free( &(mImplCtx->P) ); + mbedtls_ecp_point_free( &(mImplCtx->Q) ); + mbedtls_mpi_free( &(mImplCtx->d) ); + mbedtls_ecp_group_free( &(mImplCtx->grp) ); + mbedtls_ctr_drbg_free( &(mImplCtx->ctr_drbg) ); + mbedtls_entropy_free( &(mImplCtx->entropy) ); +} + +static mbedtls_ecp_group_id convertToMbedtlsEcpGroupId(tc::crypto::EccCurveType ec_type) +{ + mbedtls_ecp_group_id group_id = MBEDTLS_ECP_DP_NONE; + switch (ec_type) + { + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP192R1: + group_id = MBEDTLS_ECP_DP_SECP192R1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP224R1: + group_id = MBEDTLS_ECP_DP_SECP224R1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP256R1: + group_id = MBEDTLS_ECP_DP_SECP256R1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP384R1: + group_id = MBEDTLS_ECP_DP_SECP384R1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP521R1: + group_id = MBEDTLS_ECP_DP_SECP521R1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_BP256R1: + group_id = MBEDTLS_ECP_DP_BP256R1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_BP384R1: + group_id = MBEDTLS_ECP_DP_BP384R1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_BP512R1: + group_id = MBEDTLS_ECP_DP_BP512R1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_CURVE25519: + group_id = MBEDTLS_ECP_DP_CURVE25519; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP192K1: + group_id = MBEDTLS_ECP_DP_SECP192K1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP224K1: + group_id = MBEDTLS_ECP_DP_SECP224K1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP256K1: + group_id = MBEDTLS_ECP_DP_SECP256K1; + break; + case tc::crypto::EccCurveType::ECC_CURVE_TYPE_CURVE448: + group_id = MBEDTLS_ECP_DP_CURVE448; + break; + } + + return group_id; +} + +void tc::crypto::detail::EcdhSharedSecretGeneratorImpl::generateSharedSecret(EccCurveType ec_type, byte_t* z, size_t z_size, const byte_t* d, size_t d_size, const byte_t* Q, size_t Q_size) +{ + mbedtls_ecp_group_id group_id = convertToMbedtlsEcpGroupId(ec_type); + if (group_id == MBEDTLS_ECP_DP_NONE) + { + throw tc::ArgumentOutOfRangeException(kClassName, fmt::format("ec_type ({}) was not valid.", (uint32_t)ec_type)); + } + + int ret; + + // clear source d mpi + ret = mbedtls_mpi_lset( &(mImplCtx->d) , 0 ); + switch (ret) + { + case (0): + break; + default: + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_mpi_lset(d) An unexpected error occurred. {:x})", ret)); + } + + // clear source Q point + ret = mbedtls_ecp_set_zero( &(mImplCtx->Q) ); + switch (ret) + { + case (0): + break; + default: + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_ecp_set_zero(Q) An unexpected error occurred. {:x})", ret)); + } + + // clear source P point + ret = mbedtls_ecp_set_zero( &(mImplCtx->P) ); + switch (ret) + { + case (0): + break; + default: + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_ecp_set_zero(P) An unexpected error occurred. {:x})", ret)); + } + + // load ecp group + ret = mbedtls_ecp_group_load( &(mImplCtx->grp), group_id ); + switch (ret) + { + case (0): + break; + default: + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_ecp_group_load() An unexpected error occurred. {:x})", ret)); + } + + // get p_len from ecp group + size_t p_len = mbedtls_mpi_size( &(mImplCtx->grp.P) ); + + // import private component (d) + if (d_size < p_len) + { + throw tc::ArgumentOutOfRangeException(kClassName, fmt::format("d size was insufficent {} (expected {})", d_size, p_len)); + } + ret = mbedtls_mpi_read_binary( &(mImplCtx->d), d, d_size ); + switch (ret) + { + case (0): + break; + //case (MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL): + default: + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_mpi_read_binary(d) An unexpected error occurred.", ret)); + } + + // import public component (Q) + if (Q_size < (p_len * 2)) + { + throw tc::ArgumentOutOfRangeException(kClassName, fmt::format("Q size was insufficent {} (expected {})", Q_size, (p_len * 2))); + } + ret = mbedtls_mpi_read_binary( &(mImplCtx->Q.X), Q, p_len ); + switch (ret) + { + case (0): + break; + //case (MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL): + default: + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_mpi_read_binary(Q.X) An unexpected error occurred.", ret)); + } + ret = mbedtls_mpi_read_binary( &(mImplCtx->Q.Y), Q + p_len, p_len ); + switch (ret) + { + case (0): + break; + //case (MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL): + default: + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_mpi_read_binary(Q.Y) An unexpected error occurred.", ret)); + } + + // generate shared secret point (P) from private component (d) and public component (Q) + ret = mbedtls_ecp_mul( &(mImplCtx->grp), &(mImplCtx->P), &(mImplCtx->d), &(mImplCtx->Q), mbedtls_ctr_drbg_random, &(mImplCtx->ctr_drbg) ); + switch (ret) + { + case (0): + break; + case (MBEDTLS_ERR_ECP_INVALID_KEY): + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_ecp_mul() Invalid public or private key. {:x})", ret)); + default: + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_ecp_mul() An unexpected error occurred. {:x})", ret)); + } + + // export shared secret + if (z != nullptr) + { + // if key is < min size + if (z_size < p_len) + { + throw tc::ArgumentNullException(kClassName, "z was not null, z_size was insufficent to store shared secret"); + } + + ret = mbedtls_mpi_write_binary( &(mImplCtx->P.X), z, p_len ); + switch (ret) + { + case (0): + break; + //case (MBEDTLS_ERR_MPI_BUFFER_TOO_SMALL): + default: + throw tc::crypto::CryptoException(kClassName, fmt::format("mbedtls_mpi_write_binary() An unexpected error occurred. {:x})", ret)); + } + } +} \ No newline at end of file diff --git a/src/encode/Base64Util.cpp b/src/encode/Base64Util.cpp index 19842190..c8dbbdc1 100644 --- a/src/encode/Base64Util.cpp +++ b/src/encode/Base64Util.cpp @@ -8,18 +8,14 @@ inline std::string byteDataAsString(const tc::ByteData& data) { std::string str = ""; - if (data.size() > 0) + for (size_t i = 0; i < data.size(); i++) { - str = std::string((const char*)data.data()); - } - - for (size_t i = 0; i < str.size(); i++) - { - if ( ! std::isprint(static_cast(str[i])) ) + if ( ! std::isprint(static_cast(data[i])) ) { - str = ""; break; } + + str.push_back(static_cast(data[i])); } return str; diff --git a/src/os/Environment.cpp b/src/os/Environment.cpp index ecdb68a3..fab1e536 100644 --- a/src/os/Environment.cpp +++ b/src/os/Environment.cpp @@ -37,4 +37,40 @@ bool tc::os::getEnvVar(const std::string& name, std::string& value) } #endif return did_find_variable; +} + +static const std::vector kOSTempDirEnvVarList = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"}; + +void tc::os::getTempDirPath(tc::io::Path& dir_path) +{ +#if 0 +// #ifdef _WIN32 + DWORD dir_buffer_size = MAX_PATH+1; + std::shared_ptr dir_buffer(new wchar_t[dir_buffer_size]); + + DWORD dir_string_size = GetTempPath2W(dir_buffer_size, dir_buffer.get()); + + if (dir_string_size == 0) + { + throw tc::io::DirectoryNotFoundException("Operating system could not provide temporary directory."); + } + + dir_path = tc::io::Path(std::u16string(dir_buffer.get())); +#else + std::string dir_path_str = ""; + for (size_t i = 0; i < kOSTempDirEnvVarList.size(); i++) + { + if (tc::os::getEnvVar(kOSTempDirEnvVarList[i], dir_path_str) && dir_path_str != "") + { + break; + } + } + + if (dir_path_str.size() == 0) + { + throw tc::io::DirectoryNotFoundException("Operating system could not provide temporary directory."); + } + + dir_path = tc::io::Path(dir_path_str); +#endif } \ No newline at end of file diff --git a/test/crypto_EccKeyGenerator_TestClass.cpp b/test/crypto_EccKeyGenerator_TestClass.cpp new file mode 100644 index 00000000..1d36d8bb --- /dev/null +++ b/test/crypto_EccKeyGenerator_TestClass.cpp @@ -0,0 +1,519 @@ +#include "crypto_EccKeyGenerator_TestClass.h" + +#include + +#include +#include +#include + +//--------------------------------------------------------- + +crypto_EccKeyGenerator_TestClass::crypto_EccKeyGenerator_TestClass() : + mTestTag("tc::crypto::EccKeyGenerator"), + mTestResults() +{ +} + +void crypto_EccKeyGenerator_TestClass::runAllTests(void) +{ + test_Class(); + test_UtilFunc(); + test_MultipleObjectsCreateDifferentData(); + test_RepeatedCallsCreateDifferentData(); +} + +const std::string& crypto_EccKeyGenerator_TestClass::getTestTag() const +{ + return mTestTag; +} + +const std::vector& crypto_EccKeyGenerator_TestClass::getTestResults() const +{ + return mTestResults; +} + +//--------------------------------------------------------- + +void crypto_EccKeyGenerator_TestClass::test_Class() +{ + TestResult test_result; + test_result.test_name = "test_Class"; + test_result.result = "NOT RUN"; + test_result.comments = ""; + + try + { + // create class to store key + tc::crypto::EccKey key; + tc::crypto::EccKey pubkey; + + // curves to test + std::vector curve_test_list; + getCurveTestList(curve_test_list); + + tc::crypto::EccKeyGenerator keygen; + for (size_t i = 0; i < curve_test_list.size(); i++) + { + // reset key + key.curve_type = tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP192R1; + key.d = tc::ByteData(); + key.Q = tc::ByteData(); + + // generate key + keygen.generateKey(key, curve_test_list[i].curve_type); + + // check key + if (key.curve_type != curve_test_list[i].curve_type) + { + throw tc::TestException(fmt::format(".generateKey({},key) key.curve_type ({}) did not match expected value ({})", curve_test_list[i].curve_name, (uint32_t)key.curve_type, (uint32_t)curve_test_list[i].curve_type)); + } + + size_t integer_len_bytes = align(curve_test_list[i].integer_bit_size, 8) / 8; + size_t expected_d_size = integer_len_bytes; + size_t expected_Q_size = integer_len_bytes*2; + if (key.d.size() != expected_d_size) + { + throw tc::TestException(fmt::format(".generateKey({},key) key.d.size() ({}) did not match expected value ({})", curve_test_list[i].curve_name, key.d.size(), expected_d_size)); + } + + if (key.Q.size() != expected_Q_size) + { + throw tc::TestException(fmt::format(".generateKey({},key) key.Q.size() ({}) did not match expected value ({})", curve_test_list[i].curve_name, key.Q.size(), expected_Q_size)); + } + + // generate public key + keygen.generatePublicKey(pubkey, key); + + // check public key type + if (pubkey.curve_type != key.curve_type) + { + throw tc::TestException(fmt::format(".generatePublicKey(pubkey,key) (for curve {}) pubkey.curve_type ({}) did not match key.curve_type ({})", curve_test_list[i].curve_name, (uint32_t)pubkey.curve_type, (uint32_t)key.curve_type)); + } + + // check public key has no private component + if (pubkey.d.size() != 0) + { + throw tc::TestException(fmt::format(".generatePublicKey(pubkey,key) (for curve {}) pubkey.d.size() ({}) was not 0", curve_test_list[i].curve_name, pubkey.d.size())); + } + + // check public key has same public component as key + if (pubkey.Q.size() != key.Q.size()) + { + throw tc::TestException(fmt::format(".generatePublicKey(pubkey,key) (for curve {}) pubkey.Q.size() ({}) did not match key.Q.size() ({})", curve_test_list[i].curve_name, pubkey.Q.size(), key.Q.size())); + } + if (memcmp(pubkey.Q.data(), key.Q.data(), pubkey.Q.size()) != 0) + { + throw tc::TestException(fmt::format(".generatePublicKey(pubkey,key) (for curve {}) pubkey.Q.data() did not match key.Q.data()", curve_test_list[i].curve_name)); + } + } + + // record result + test_result.result = "PASS"; + test_result.comments = ""; + } + catch (const tc::TestException& e) + { + // record result + test_result.result = "FAIL"; + test_result.comments = e.what(); + } + catch (const std::exception& e) + { + // record result + test_result.result = "UNHANDLED EXCEPTION"; + test_result.comments = e.what(); + } + + // add result to list + mTestResults.push_back(std::move(test_result)); +} + +void crypto_EccKeyGenerator_TestClass::test_UtilFunc() +{ + TestResult test_result; + test_result.test_name = "test_UtilFunc"; + test_result.result = "NOT RUN"; + test_result.comments = ""; + + try + { + // create class to store key + tc::crypto::EccKey key; + tc::crypto::EccKey pubkey; + + // curves to test + std::vector curve_test_list; + getCurveTestList(curve_test_list); + + for (size_t i = 0; i < curve_test_list.size(); i++) + { + // reset key + key.curve_type = tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP192R1; + key.d = tc::ByteData(); + key.Q = tc::ByteData(); + + // generate key + tc::crypto::GenerateEccKey(key, curve_test_list[i].curve_type); + + // check key + if (key.curve_type != curve_test_list[i].curve_type) + { + throw tc::TestException(fmt::format("tc::crypto::GenerateEccKey({},key) key.curve_type ({}) did not match expected value ({})", curve_test_list[i].curve_name, (uint32_t)key.curve_type, (uint32_t)curve_test_list[i].curve_type)); + } + + size_t integer_len_bytes = align(curve_test_list[i].integer_bit_size, 8) / 8; + size_t expected_d_size = integer_len_bytes; + size_t expected_Q_size = integer_len_bytes*2; + if (key.d.size() != expected_d_size) + { + throw tc::TestException(fmt::format("tc::crypto::GenerateEccKey({},key) key.d.size() ({}) did not match expected value ({})", curve_test_list[i].curve_name, key.d.size(), expected_d_size)); + } + + if (key.Q.size() != expected_Q_size) + { + throw tc::TestException(fmt::format("tc::crypto::GenerateEccKey({},key) key.Q.size() ({}) did not match expected value ({})", curve_test_list[i].curve_name, key.Q.size(), expected_Q_size)); + } + + // generate public key + tc::crypto::GenerateEccPublicKey(pubkey, key); + + // check public key type + if (pubkey.curve_type != key.curve_type) + { + throw tc::TestException(fmt::format("tc::crypto::GenerateEccPublicKey(pubkey,key) (for curve {}) pubkey.curve_type ({}) did not match key.curve_type ({})", curve_test_list[i].curve_name, (uint32_t)pubkey.curve_type, (uint32_t)key.curve_type)); + } + + // check public key has no private component + if (pubkey.d.size() != 0) + { + throw tc::TestException(fmt::format("tc::crypto::GenerateEccPublicKey(pubkey,key) (for curve {}) pubkey.d.size() ({}) was not 0", curve_test_list[i].curve_name, pubkey.d.size())); + } + + // check public key has same public component as key + if (pubkey.Q.size() != key.Q.size()) + { + throw tc::TestException(fmt::format("tc::crypto::GenerateEccPublicKey(pubkey,key) (for curve {}) pubkey.Q.size() ({}) did not match key.Q.size() ({})", curve_test_list[i].curve_name, pubkey.Q.size(), key.Q.size())); + } + if (memcmp(pubkey.Q.data(), key.Q.data(), pubkey.Q.size()) != 0) + { + throw tc::TestException(fmt::format("tc::crypto::GenerateEccPublicKey(pubkey,key) (for curve {}) pubkey.Q.data() did not match key.Q.data()", curve_test_list[i].curve_name)); + } + } + + // record result + test_result.result = "PASS"; + test_result.comments = ""; + } + catch (const tc::TestException& e) + { + // record result + test_result.result = "FAIL"; + test_result.comments = e.what(); + } + catch (const std::exception& e) + { + // record result + test_result.result = "UNHANDLED EXCEPTION"; + test_result.comments = e.what(); + } + + // add result to list + mTestResults.push_back(std::move(test_result)); +} + +void crypto_EccKeyGenerator_TestClass::test_MultipleObjectsCreateDifferentData() +{ + TestResult test_result; + test_result.test_name = "test_MultipleObjectsCreateDifferentData"; + test_result.result = "NOT RUN"; + test_result.comments = ""; + + try + { + // create class to store key + tc::crypto::EccKey key1, key2, key3; + + // curves to test + std::vector curve_test_list; + getCurveTestList(curve_test_list); + + // test for each curve type + tc::crypto::EccKeyGenerator keygen; + for (size_t i = 0; i < curve_test_list.size(); i++) + { + // generate keys + tc::crypto::EccKeyGenerator keygen1, keygen2, keygen3; + keygen1.generateKey(key1, curve_test_list[i].curve_type); + keygen2.generateKey(key2, curve_test_list[i].curve_type); + keygen3.generateKey(key3, curve_test_list[i].curve_type); + + static const size_t kSimilarityThreshold = 3; + + // check private component + size_t privateCmp12 = 0, privateCmp13 = 0, privateCmp23 = 0; + + for (size_t i = 0; i < key1.d.size(); i++) + { + privateCmp12 += key1.d[i] == key2.d[i]; + privateCmp13 += key1.d[i] == key3.d[i]; + privateCmp23 += key2.d[i] == key3.d[i]; + } + + // check to see if any of the tests were similar + if (privateCmp12 > kSimilarityThreshold) + { + throw tc::TestException(fmt::format("(curve {}) privateKey 1 & privateKey 2 has {:d} similar bytes ({} vs {})", curve_test_list[i].curve_name, privateCmp12, tc::cli::FormatUtil::formatBytesAsString(key1.d, false, ""), tc::cli::FormatUtil::formatBytesAsString(key2.d, false, ""))); + } + if (privateCmp13 > kSimilarityThreshold) + { + throw tc::TestException(fmt::format("(curve {}) privateKey 1 & privateKey 3 has {:d} similar bytes ({} vs {})", curve_test_list[i].curve_name, privateCmp13, tc::cli::FormatUtil::formatBytesAsString(key1.d, false, ""), tc::cli::FormatUtil::formatBytesAsString(key2.d, false, ""))); + } + if (privateCmp23 > kSimilarityThreshold) + { + throw tc::TestException(fmt::format("(curve {}) privateKey 2 & privateKey 3 has {:d} similar bytes ({} vs {})", curve_test_list[i].curve_name, privateCmp23, tc::cli::FormatUtil::formatBytesAsString(key1.d, false, ""), tc::cli::FormatUtil::formatBytesAsString(key2.d, false, ""))); + } + + // check public component + size_t publicCmp12 = 0, publicCmp13 = 0, publicCmp23 = 0; + + for (size_t i = 0; i < key1.Q.size(); i++) + { + if (!curve_test_list[i].curve_defines_Q_y && i >= (key1.Q.size()/2)) + break; + + publicCmp12 += key1.Q[i] == key2.Q[i]; + publicCmp13 += key1.Q[i] == key3.Q[i]; + publicCmp23 += key2.Q[i] == key3.Q[i]; + } + + // check to see if any of the tests were similar + if (publicCmp12 > kSimilarityThreshold) + { + throw tc::TestException(fmt::format("(curve {}) publicKey 1 & publicKey 2 has {:d} similar bytes ({} vs {})", curve_test_list[i].curve_name, publicCmp12, tc::cli::FormatUtil::formatBytesAsString(key1.Q, false, ""), tc::cli::FormatUtil::formatBytesAsString(key2.Q, false, ""))); + } + if (publicCmp13 > kSimilarityThreshold) + { + throw tc::TestException(fmt::format("(curve {}) publicKey 1 & publicKey 3 has {:d} similar bytes ({} vs {})", curve_test_list[i].curve_name, publicCmp13, tc::cli::FormatUtil::formatBytesAsString(key1.Q, false, ""), tc::cli::FormatUtil::formatBytesAsString(key2.Q, false, ""))); + } + if (publicCmp23 > kSimilarityThreshold) + { + throw tc::TestException(fmt::format("(curve {}) publicKey 2 & publicKey 3 has {:d} similar bytes ({} vs {})", curve_test_list[i].curve_name, publicCmp23, tc::cli::FormatUtil::formatBytesAsString(key1.Q, false, ""), tc::cli::FormatUtil::formatBytesAsString(key2.Q, false, ""))); + } + } + + // record result + test_result.result = "PASS"; + test_result.comments = ""; + } + catch (const tc::TestException& e) + { + // record result + test_result.result = "FAIL"; + test_result.comments = e.what(); + } + catch (const std::exception& e) + { + // record result + test_result.result = "UNHANDLED EXCEPTION"; + test_result.comments = e.what(); + } + + // add result to list + mTestResults.push_back(std::move(test_result)); +} + +void crypto_EccKeyGenerator_TestClass::test_RepeatedCallsCreateDifferentData() +{ + TestResult test_result; + test_result.test_name = "test_RepeatedCallsCreateDifferentData"; + test_result.result = "NOT RUN"; + test_result.comments = ""; + + try + { + // create class to store key + tc::crypto::EccKey key1, key2, key3; + + // curves to test + std::vector curve_test_list; + getCurveTestList(curve_test_list); + + // test for each curve type + for (size_t i = 0; i < curve_test_list.size(); i++) + { + // generate keys + tc::crypto::EccKeyGenerator keygen; + keygen.generateKey(key1, curve_test_list[i].curve_type); + keygen.generateKey(key2, curve_test_list[i].curve_type); + keygen.generateKey(key3, curve_test_list[i].curve_type); + + static const size_t kSimilarityThreshold = 3; + + // check private component + size_t privateCmp12 = 0, privateCmp13 = 0, privateCmp23 = 0; + + for (size_t i = 0; i < key1.d.size(); i++) + { + privateCmp12 += key1.d[i] == key2.d[i]; + privateCmp13 += key1.d[i] == key3.d[i]; + privateCmp23 += key2.d[i] == key3.d[i]; + } + + // check to see if any of the tests were similar + if (privateCmp12 > kSimilarityThreshold) + { + throw tc::TestException(fmt::format("(curve {}) privateKey 1 & privateKey 2 has {:d} similar bytes ({} vs {})", curve_test_list[i].curve_name, privateCmp12, tc::cli::FormatUtil::formatBytesAsString(key1.d, false, ""), tc::cli::FormatUtil::formatBytesAsString(key2.d, false, ""))); + } + if (privateCmp13 > kSimilarityThreshold) + { + throw tc::TestException(fmt::format("(curve {}) privateKey 1 & privateKey 3 has {:d} similar bytes ({} vs {})", curve_test_list[i].curve_name, privateCmp13, tc::cli::FormatUtil::formatBytesAsString(key1.d, false, ""), tc::cli::FormatUtil::formatBytesAsString(key2.d, false, ""))); + } + if (privateCmp23 > kSimilarityThreshold) + { + throw tc::TestException(fmt::format("(curve {}) privateKey 2 & privateKey 3 has {:d} similar bytes ({} vs {})", curve_test_list[i].curve_name, privateCmp23, tc::cli::FormatUtil::formatBytesAsString(key1.d, false, ""), tc::cli::FormatUtil::formatBytesAsString(key2.d, false, ""))); + } + + // check public component + size_t publicCmp12 = 0, publicCmp13 = 0, publicCmp23 = 0; + + for (size_t i = 0; i < key1.Q.size(); i++) + { + if (!curve_test_list[i].curve_defines_Q_y && i >= (key1.Q.size()/2)) + break; + + publicCmp12 += key1.Q[i] == key2.Q[i]; + publicCmp13 += key1.Q[i] == key3.Q[i]; + publicCmp23 += key2.Q[i] == key3.Q[i]; + } + + // check to see if any of the tests were similar + if (publicCmp12 > kSimilarityThreshold) + { + throw tc::TestException(fmt::format("(curve {}) publicKey 1 & publicKey 2 has {:d} similar bytes ({} vs {})", curve_test_list[i].curve_name, publicCmp12, tc::cli::FormatUtil::formatBytesAsString(key1.Q, false, ""), tc::cli::FormatUtil::formatBytesAsString(key2.Q, false, ""))); + } + if (publicCmp13 > kSimilarityThreshold) + { + throw tc::TestException(fmt::format("(curve {}) publicKey 1 & publicKey 3 has {:d} similar bytes ({} vs {})", curve_test_list[i].curve_name, publicCmp13, tc::cli::FormatUtil::formatBytesAsString(key1.Q, false, ""), tc::cli::FormatUtil::formatBytesAsString(key2.Q, false, ""))); + } + if (publicCmp23 > kSimilarityThreshold) + { + throw tc::TestException(fmt::format("(curve {}) publicKey 2 & publicKey 3 has {:d} similar bytes ({} vs {})", curve_test_list[i].curve_name, publicCmp23, tc::cli::FormatUtil::formatBytesAsString(key1.Q, false, ""), tc::cli::FormatUtil::formatBytesAsString(key2.Q, false, ""))); + } + } + + // record result + test_result.result = "PASS"; + test_result.comments = ""; + } + catch (const tc::TestException& e) + { + // record result + test_result.result = "FAIL"; + test_result.comments = e.what(); + } + catch (const std::exception& e) + { + // record result + test_result.result = "UNHANDLED EXCEPTION"; + test_result.comments = e.what(); + } + + // add result to list + mTestResults.push_back(std::move(test_result)); +} + +void crypto_EccKeyGenerator_TestClass::getCurveTestList(std::vector& curve_test_list) const +{ + curve_test_list.clear(); + + curve_test_list.push_back({ + "ECC_CURVE_TYPE_SECP192R1", + tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP192R1, + 192, + true + }); + + curve_test_list.push_back({ + "ECC_CURVE_TYPE_SECP224R1", + tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP224R1, + 224, + true + }); + + curve_test_list.push_back({ + "ECC_CURVE_TYPE_SECP256R1", + tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP256R1, + 256, + true + }); + + curve_test_list.push_back({ + "ECC_CURVE_TYPE_SECP384R1", + tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP384R1, + 384, + true + }); + + curve_test_list.push_back({ + "ECC_CURVE_TYPE_SECP521R1", + tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP521R1, + 521, + true + }); + + curve_test_list.push_back({ + "ECC_CURVE_TYPE_BP256R1", + tc::crypto::EccCurveType::ECC_CURVE_TYPE_BP256R1, + 256, + true + }); + + curve_test_list.push_back({ + "ECC_CURVE_TYPE_BP384R1", + tc::crypto::EccCurveType::ECC_CURVE_TYPE_BP384R1, + 384, + true + }); + + curve_test_list.push_back({ + "ECC_CURVE_TYPE_BP512R1", + tc::crypto::EccCurveType::ECC_CURVE_TYPE_BP512R1, + 512, + true + }); + + curve_test_list.push_back({ + "ECC_CURVE_TYPE_CURVE25519", + tc::crypto::EccCurveType::ECC_CURVE_TYPE_CURVE25519, + 255, + false + }); + + curve_test_list.push_back({ + "ECC_CURVE_TYPE_SECP192K1", + tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP192K1, + 192, + true + }); + + curve_test_list.push_back({ + "ECC_CURVE_TYPE_SECP224K1", + tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP224K1, + 224, + true + }); + + curve_test_list.push_back({ + "ECC_CURVE_TYPE_SECP256K1", + tc::crypto::EccCurveType::ECC_CURVE_TYPE_SECP256K1, + 256, + true + }); + + curve_test_list.push_back({ + "ECC_CURVE_TYPE_CURVE448", + tc::crypto::EccCurveType::ECC_CURVE_TYPE_CURVE448, + 448, + false + }); +} diff --git a/test/crypto_EccKeyGenerator_TestClass.h b/test/crypto_EccKeyGenerator_TestClass.h new file mode 100644 index 00000000..bde5663c --- /dev/null +++ b/test/crypto_EccKeyGenerator_TestClass.h @@ -0,0 +1,38 @@ +#pragma once +#include "ITestClass.h" +#include + +class crypto_EccKeyGenerator_TestClass : public ITestClass +{ +public: + crypto_EccKeyGenerator_TestClass(); + + // this will run the tests + void runAllTests(); + + // this is the label for this test (for filtering purposes) + const std::string& getTestTag() const; + + // this is where the test results are written + const std::vector& getTestResults() const; +private: + std::string mTestTag; + std::vector mTestResults; + + void test_sbox(); + + void test_Class(); + void test_UtilFunc(); + + void test_MultipleObjectsCreateDifferentData(); + void test_RepeatedCallsCreateDifferentData(); + + struct sCurveInfo { + std::string curve_name; + tc::crypto::EccCurveType curve_type; + size_t integer_bit_size; + bool curve_defines_Q_y; + }; + + void getCurveTestList(std::vector& curve_test_list) const; +}; \ No newline at end of file diff --git a/test/main.cpp b/test/main.cpp index 4f85297a..ac807eaf 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -94,6 +94,7 @@ #include "crypto_Rsa1024PssSha2512Signer_TestClass.h" #include "crypto_Rsa2048PssSha2512Signer_TestClass.h" #include "crypto_Rsa4096PssSha2512Signer_TestClass.h" +#include "crypto_EccKeyGenerator_TestClass.h" #include "ITestClass.h" @@ -130,8 +131,10 @@ void runTest(std::vector& global_test_results, const std::regex global_test_results.push_back(std::move(local_test_results)); } -void outputResultsToStdout(const std::vector& global_test_results, const std::regex& include_result_regex, const std::regex& exclude_result_regex) +int outputResultsToStdout(const std::vector& global_test_results, const std::regex& include_result_regex, const std::regex& exclude_result_regex) { + int res = 0; + for (auto test_class_itr = global_test_results.begin(); test_class_itr != global_test_results.end(); test_class_itr++) { size_t total_tests = 0, total_passed_tests = 0; @@ -154,7 +157,13 @@ void outputResultsToStdout(const std::vector& global_test_resul fmt::print("\n"); } fmt::print("[{:s}] END ({:d}/{:d} passed)\n", test_class_itr->tag, total_passed_tests, total_tests); + + // change return value if tests failed + if (total_passed_tests < total_tests) + res -= 1; } + + return res; } int main(int argc, char** argv) @@ -340,7 +349,8 @@ int main(int argc, char** argv) runTest(global_test_results, include_test_regex, exclude_test_regex, include_slow_tests); runTest(global_test_results, include_test_regex, exclude_test_regex, include_slow_tests); runTest(global_test_results, include_test_regex, exclude_test_regex, include_slow_tests); + runTest(global_test_results, include_test_regex, exclude_test_regex, include_slow_tests); // output results - outputResultsToStdout(global_test_results, include_result_regex, exclude_result_regex); + return outputResultsToStdout(global_test_results, include_result_regex, exclude_result_regex); } \ No newline at end of file