From 4a2c18751a4e267de0c3af63b6c789a8baf79e97 Mon Sep 17 00:00:00 2001 From: Kyle Isom Date: Wed, 18 Oct 2023 03:39:27 -0700 Subject: [PATCH] Cut a release. --- .clang-tidy | 4 +- .idea/inspectionProfiles/Project_Default.xml | 4 +- CMakeLists.txt | 10 +- cmake/docs.cmake | 4 +- docs/mainpage.md | 24 +- emsha.cc | 8 +- emsha/emsha.h | 128 +++++---- emsha/hmac.h | 218 +++++++-------- emsha/sha256.h | 270 +++++++++---------- hmac.cc | 96 +++---- sha256.cc | 194 +++++++------ test_emsha.cc | 54 ++-- test_mem.cc | 26 +- test_sha256.cc | 28 +- test_utils.cc | 64 ++--- test_utils.h | 4 +- 16 files changed, 593 insertions(+), 543 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 183b1d8..1fe1cd5 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,7 +1,8 @@ +HeaderFilterRegex: \./.+ + Checks: >- bugprone-*, cppcoreguidelines-*, - google-*, misc-*, modernize-*, performance-*, @@ -15,6 +16,7 @@ Checks: >- -cppcoreguidelines-pro-type-vararg, -google-readability-braces-around-statements, -google-readability-function-size, + -google-readability-namespace-comments, -misc-no-recursion, -modernize-return-braced-init-list, -modernize-use-nodiscard, diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml index 536a3da..41d7934 100644 --- a/.idea/inspectionProfiles/Project_Default.xml +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -2,8 +2,8 @@ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 90f2778..8be2ab0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.22) project(emsha - VERSION 1.0.3 + VERSION 1.1.0 LANGUAGES CXX DESCRIPTION "A compact HMAC-SHA-256 C++11 library.") set(CMAKE_CXX_STANDARD 11) @@ -9,8 +9,14 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(EMSHA_NO_HEXSTRING OFF CACHE BOOL "Don't include support for hex strings.") -set(EMSHA_NO_HEXLUT OFF CACHE BOOL +if (EMSHA_NO_HEXSTRING) + add_definitions(EMSHA_NO_HEXSTRING) +endif () +set(SET_EMSHA_NO_HEXLUT OFF CACHE BOOL "Don't use a LUT for hex strings (saves ~256B of memory).") +if (SET_EMSHA_NO_HEXLUT) + add_definitions("-DEMSHA_NO_HEXLUT") +endif () include(CTest) enable_testing() diff --git a/cmake/docs.cmake b/cmake/docs.cmake index af153ff..309530e 100644 --- a/cmake/docs.cmake +++ b/cmake/docs.cmake @@ -24,13 +24,13 @@ if (${DOXYGEN_FOUND}) set(DOXYGEN_GENERATE_LATEX YES) set(DOXYGEN_EXTRACT_ALL YES) set(DOXYGEN_USE_MDFILE_AS_MAINPAGE "${CMAKE_CURRENT_SOURCE_DIR}/docs/mainpage.md") + set(DOXYGEN_EXCLUDE_PATTERNS "test_*" "*.cc" ) message(STATUS "Doxygen found, building docs.") doxygen_add_docs(${PROJECT_NAME}_docs ${HEADER_FILES} - ${SOURCE_FILES} + ALL USE_STAMP_FILE) - add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}_docs) install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html ${CMAKE_CURRENT_BINARY_DIR}/latex DESTINATION share/doc/${PROJECT_NAME}/doxygen) diff --git a/docs/mainpage.md b/docs/mainpage.md index 12194cd..ca088a5 100644 --- a/docs/mainpage.md +++ b/docs/mainpage.md @@ -77,50 +77,50 @@ satisfy a common interface :cpp`Hash`{.interpreted-text role="class"}. All functionality provided by this library is found under the `emsha` namespace. -### `EMSHA_RESULT` +### `EMSHAResult` -The `EMSHA_RESULT` enum is used to convey the result of an operation. +The `EMSHAResult` enum is used to convey the result of an operation. The possible values are: // All operations have completed successfully so far. - EMSHA_ROK = 0, + EMSHAResult::OK = 0, // A self test or unit test failed. - EMSHA_TEST_FAILURE = 1, + EMSHAResult::TestFailure = 1, // A null pointer was passed in as a buffer where it // shouldn't have been. - EMSHA_NULLPTR = 2, + EMSHAResult::NullPointer = 2, // The Hash is in an invalid state. - EMSHA_INVALID_STATE = 3, + EMSHAResult::InvalidState = 3, // The input to SHA256::update is too large. - SHA256_INPUT_TOO_LONG = 4, + EMSHAResult::InputTooLong = 4, // The self tests have been disabled, but a self test // function was called. - EMSHA_SELFTEST_DISABLED = 5 + EMSHAResult::SelfTestDisabled = 5 As a convenience, the following `typedef` is also provided. -> `typedef enum _EMSHA_RESULT_` :cpp`EMSHA_RESULT`{.interpreted-text +> `typedef enum _EMSHA_RESULT_` :cpp`EMSHAResult`{.interpreted-text > role="type"} ## The Hash interface In general, a [Hash]{.title-ref} is used along the lines of: : - emsha::EMSHA_RESULT + emsha::EMSHAResult hash_single_pass(uint8_t *m, uint32_t ml, uint8_t *digest) { // Depending on the implementation, the constructor may need // arguments. emsha::Hash h; - emsha::EMSHA_RESULT res; + emsha::EMSHAResult res; res = h.write(m, ml); - if (emsha::EMSHA_ROK != res) { + if (emsha::EMSHAResult::OK != res) { return res; } diff --git a/emsha.cc b/emsha.cc index b590b07..f89c581 100644 --- a/emsha.cc +++ b/emsha.cc @@ -55,10 +55,13 @@ HashEqual(const uint8_t *a, const uint8_t *b) #ifndef EMSHA_NO_HEXSTRING + + +namespace { #ifndef EMSHA_NO_HEXLUT // If using a lookup table is permitted, then the faster way to do this // is to use one. -static void +void writeHexChar(uint8_t *dest, uint8_t src) { static constexpr uint8_t lut[256][3] = { @@ -106,7 +109,7 @@ writeHexChar(uint8_t *dest, uint8_t src) // memory constraints, we'll work around this using a small (16-byte) // lookup table and some bit shifting. On platforms where even this is // too much, the HexString functionality will just be disabled. -static void +void writeHexChar(uint8_t *dest, uint8_t src) { static constexpr uint8_t lut[] = { @@ -118,6 +121,7 @@ writeHexChar(uint8_t *dest, uint8_t src) *(dest + 1) = lut[(src & 0xF)]; } #endif // #ifndef EMSHA_NO_HEXLUT +} // anonymous namespace for writeHexChar void diff --git a/emsha/emsha.h b/emsha/emsha.h index 5587ebb..a4a2b3a 100644 --- a/emsha/emsha.h +++ b/emsha/emsha.h @@ -35,11 +35,10 @@ #include -// Emsha is an EMbedded Secure HAshing interface. +// emsha is an EMbedded Secure HAshing interface. namespace emsha { -/// EMSHA_CHECK is used for sanity checks in certain parts of the code. #ifdef NDEBUG /// EMSHA_CHECK is used for sanity checks in certain parts of /// the code. If asserts are turned off, expand the check to an @@ -58,43 +57,52 @@ namespace emsha { const std::uint32_t SHA256_HASH_SIZE = 32; -/// The EMSHA_RESULT type is used to indicate whether an -/// operation succeeded, and if not, what the general fault type -/// was. -typedef enum _EMSHA_RESULT_ : std::uint8_t { - /// All operations have completed successfully so far. - EMSHA_ROK = 0, +/// \brief Describe the result of an EMSHA operation. +/// +/// The EMSHAResult type is used to indicate whether an operation +/// succeeded, and if not, what the general fault type was. +typedef enum class EMSHAResult : std::uint8_t { + /// An unknown fault occurred. This is a serious bug in the + /// program. + Unknown = 0, - /// A self test or unit test failed. - EMSHA_TEST_FAILURE = 1, + /// All operations have completed successfully so far. + OK = 1, + + /// The self-test failed. + TestFailure = 2, /// A null pointer was passed in as a buffer where it shouldn't /// have been. - EMSHA_NULLPTR = 2, + NullPointer = 3, /// The Hash is in an invalid state. - EMSHA_INVALID_STATE = 3, + InvalidState = 4, /// The input to SHA256::update is too large. - SHA256_INPUT_TOO_LONG = 4, + InputTooLong = 5, /// The self tests have been disabled, but a self-test function /// was called. - EMSHA_SELFTEST_DISABLED = 5 -} EMSHA_RESULT; + SelfTestDisabled = 6 +} ; -// A Hash is generalised superclass supporting concrete classes -// that produce digests of data. +/// A Hash is an abstract base class supporting concrete classes +/// that produce digests of data. class Hash { public: virtual ~Hash() = default; - /// Reset should bring the Hash back into its - /// initial state. That is, the idea is that + /// \brief Bring the Hash back to its initial state. /// - /// hash->reset(); hash->update(...)... ; - /// hash->result(...) ; + /// That is, the idea is that + /// + /// ``` + /// hash->reset(); + /// hash->update(...); + /// hash->result(...); + /// ``` /// /// is idempotent, assuming the inputs to update /// and result are constant. The implications of @@ -103,34 +111,54 @@ public: /// in general, it has the effect of preserving /// any initial state while removing any data /// written to the Hash via the update method. - virtual EMSHA_RESULT Reset() = 0; + /// + /// \return An ::EMSHAResult describing the status of the + /// operation. + virtual EMSHAResult Reset() = 0; - // Update is writes message data into the Hash. - virtual EMSHA_RESULT Update(const std::uint8_t *m, - std::uint32_t ml) = 0; + /// \brief Write message data into the Hash. + /// + /// \param message The message data to write into the Hash. + /// \param messageLength The length of the message data. + /// \return An ::EMSHAResult describing the status of the + /// operation. + virtual EMSHAResult Update(const std::uint8_t *message, + std::uint32_t messageLength) = 0; - /// Finalise carries out any final operations - /// on the Hash; after a call to finalize, no - /// more data can be written. Additionally, it - /// transfers out the resulting hash into its + /// \brief Carry out any final operations on the Hash. + /// + /// After a call to finalize, no more data can be written. + /// Additionally, it transfers out the resulting hash into its /// argument. - virtual EMSHA_RESULT Finalise(std::uint8_t *d) = 0; + /// + /// \param digest The buffer to store the hash in. + /// \return An ::EMSHAResult describing the status of the + /// operation. + virtual EMSHAResult Finalise(std::uint8_t *digest) = 0; - /// Result transfers out the hash to the - /// argument. The Hash must keep enough state - /// for repeated calls to result to work. - virtual EMSHA_RESULT Result(std::uint8_t *d) = 0; + /// \brief Result transfers out the hash to the argument. + /// + /// The Hash must keep enough state for repeated calls to + /// result to work. + /// + /// \param digest The buffer to store the hash in. + /// \return An ::EMSHAResult describing the status of the + /// operation. + virtual EMSHAResult Result(std::uint8_t *digest) = 0; - /// Size should return the output size of the - /// Hash; this is, how large the buffers written - /// to by result should be. + /// \brief Return the output size of the Hash. + /// + /// This is how large the buffers written to by result should + /// be. virtual std::uint32_t Size() = 0; }; -/// HashEqual provides a constant time function for comparing -/// two hashes. The caller *must* ensure that both a and b are -/// the same size. The recommended approach is to use fixed-size -/// buffers of emsha::SHA256_HASH_SIZE length: +/// \brief Constant-time function for comparing two digests. +/// +/// HashEqual provides a constant time function for comparing two +/// digests. The caller *must* ensure that both a and b are the same +/// size. The recommended approach is to use fixed-size buffers of +/// emsha::SHA256_HASH_SIZE length: /// /// ```c++ /// uint8_t expected[emsha::SHA256_HASH_SIZE]; @@ -146,21 +174,21 @@ public: /// \param a A byte buffer of size Hash::Size(). /// \param b A byte buffer of size Hash::Size(). /// \return True if both byte arrays match. -bool -HashEqual(const std::uint8_t *a, const std::uint8_t *b); +bool HashEqual(const std::uint8_t *a, const std::uint8_t *b); + #ifndef EMSHA_NO_HEXSTRING -/// HexString writes a hex-encoded version of the src byte -/// array into dest. The caller *must* ensure that dest is -/// srclen * 2 bytes or longer. +/// \brief Write a hex-encoded version of a byte string. /// +/// HexString writes a hex-encoded version of the src byte array into +/// dest. The caller **must** ensure that dest is `srclen * 2` bytes +/// or longer. /// -/// \param dest The destination byte array at least (2*srclen) +/// \param dest The destination byte array at least (`2*srclen`) /// bytes in length. -/// \param src A byte array containing the data to hexify.. +/// \param src A byte array containing the data to hexify. /// \param srclen The size in bytes of src. -void -HexString(std::uint8_t *dest, std::uint8_t *src, std::uint32_t srclen); +void HexString(std::uint8_t *dest, std::uint8_t *src, std::uint32_t srclen); #endif // EMSHA_NO_HEXSTRING diff --git a/emsha/hmac.h b/emsha/hmac.h index b8d540b..06f30db 100644 --- a/emsha/hmac.h +++ b/emsha/hmac.h @@ -43,151 +43,137 @@ const uint32_t HMAC_KEY_LENGTH = SHA256_MB_SIZE; /// HMAC is a keyed hash that can be used to produce an /// authenticated hash of some data. The HMAC is built on -/// (and uses internally) the SHA-256 class; it's helpful to +/// (and uses internally) the SHA256 class; it's helpful to /// note that faults that occur in the SHA-256 code will be /// propagated up as the return value from many of the HMAC /// functions. class HMAC : Hash { public: - /// An HMAC is constructed with a key and the - /// length of the key. This key is stored in - /// the HMAC context, and is wiped by the HMAC - /// destructor. + /// \brief Construct an HMAC with its initial key. + /// + /// An HMAC is constructed with a key and the length of the + /// key. This key is stored in the HMAC context, and is wiped + /// by the HMAC destructor. /// /// \param k The HMAC key. /// \param kl THe length of the HMAC key. HMAC(const uint8_t *k, uint32_t kl); - /// Reset clears any data written to the HMAC; - /// this is equivalent to constructing a new HMAC, - /// but it preserves the keys. + /// \brief Clear any data written to the HMAC. /// - /// \return EMSHA_ROK is returned if the reset - /// occurred without (detected) fault. - /// If a fault occurs with the under- - /// lying SHA-256 context, the error - /// code is returned. - EMSHA_RESULT Reset() override; + /// This is equivalent to constructing a new HMAC, but it + /// preserves the keys. + /// + /// \return EMSHAResult::OK is returned if the reset occurred + /// without (detected) fault. If a fault occurs with + /// the underlying SHA256 context, the error code is + /// returned. + EMSHAResult Reset() override; - /// Update writes data into the context. While - /// there is an upper limit on the size of data - /// that the underlying hash can operate on, - /// this package is designed for small systems - /// that will not approach that level of data - /// (which is on the order of 2 exabytes), so it - /// is not thought to be a concern. + /// \brief Write data into the context. /// - /// \param m A byte array containing the message + /// While there is an upper limit on the size of data that the + /// underlying hash can operate on, this package is designed + /// for small systems that will not approach that level of data + /// (which is on the order of 2 exabytes), so it is not a + /// concern for this library. + /// + /// \param message A byte array containing the message /// to be written. - /// \param ml The message length, in bytes. - /// \return - /// - EMSHA_NULLPTR is returned if m is NULL and ml is - /// nonzero. - /// - EMSHA_INVALID_STATE is returned if the update - /// is called after a call to finalize. - /// - SHA256_INPUT_TOO_LONG is returned if too much - /// data has been written to the context. - /// - EMSHA_ROK is returned if the data was - /// successfully written into the HMAC context. + /// \param messageLength The message length, in bytes. + /// \return An ::EMSHAResult describing the result of the + /// operation. /// - EMSHA_RESULT Update(const uint8_t *, uint32_t) override; + /// - EMSHAResult::NullPointer is returned if m is NULL + /// and ml is nonzero. + /// - EMSHAResult::InvalidState is returned if the + /// update is called after a call to finalize. + /// - EMSHAResult::InputTooLong is returned if too much + /// data has been written to the context. + /// - EMSHAResult::OK is returned if the data was + /// successfully written into the HMAC context. + EMSHAResult Update(const std::uint8_t *message, std::uint32_t messageLength) override; - // Finalise completes the HMAC computation. Once this - // method is called, the context cannot be updated - // unless the context is reset. - // - // Inputs: - // d: a byte buffer that must be at least - // HMAC.size() in length. - // - // Outputs: - // EMSHA_NULLPTR is returned if d is the null - // pointer. - // - // EMSHA_INVALID_STATE is returned if the HMAC - // context is in an invalid state, such as if there - // were errors in previous updates. - // - // EMSHA_ROK is returned if the context was - // successfully finalised and the digest copied to - // d. - // - EMSHA_RESULT Finalise(uint8_t *) override; + /// \brief Complete the HMAC computation. + /// + /// \note Once #Finalise is called, the context cannot be + /// updated unless the context is reset. + /// + /// \param digest A byte buffer that must be at least #HMAC.Size() + /// in length. + /// \return An EMSHAResult describing the result of this + /// method: + /// + /// - EMSHAResult::NullPointer is returned if d is a + /// null pointer. + /// - EMSHAResult::InvalidState is returned if the HMAC + /// context is in an invalid state, such as if there + /// were errors in previous updates. + /// - EMSHAResult::OK is returned if the context was + /// successfully finalised and the digest copied to d. + /// + EMSHAResult Finalise(std::uint8_t *digest) override; - // Result copies the result from the HMAC context into - // the buffer pointed to by d, running finalize if - // needed. Once called, the context cannot be updated - // until the context is reset. - // - // Inputs: - // d: a byte buffer that must be at least - // HMAC.size() in length. - // - // Outputs: - // EMSHA_NULLPTR is returned if d is the null - // pointer. - // - // EMSHA_INVALID_STATE is returned if the HMAC - // context is in an invalid state, such as if there - // were errors in previous updates. - // - // EMSHA_ROK is returned if the context was - // successfully finalised and the digest copied to - // d. - // - EMSHA_RESULT Result(uint8_t *) override; + /// \brief Copy the current digest into a destination buffer. + /// + /// Copy the current digest from the HMAC context into + /// `digest`, running #Finalise if needed. Once called, the + /// context cannot be updated until the context is reset. + /// + /// \param digest A byte buffer that must be at least #HMAC.size() + /// in length. + /// \return An ::EMSHAResult describing the result of this + /// method: + /// + /// - EMSHAResult::NullPointer is returned if d is a + /// null pointer. + /// - EMSHAResult::InvalidState is returned if the HMAC + /// context is in an invalid state, such as if there + /// were errors in previous updates. + /// - EMSHAResult::OK is returned if the context was + /// successfully finalised and the digest copied to d. + EMSHAResult Result(std::uint8_t *digest) override; - // size returns the output size of HMAC-SHA-256, e.g. - // the size that the buffers passed to finalize and - // result should be. - // - // Outputs: - // A uint32_t representing the expected size - // of buffers passed to result and finalize. + /// \brief Returns the output size of HMAC-SHA-256. + /// + /// The buffers passed to #Update and #Finalise should be at + /// least this size. + /// + /// \return The expected size of buffers passed to result and + /// finalize. std::uint32_t Size() override; - // When an HMAC context is destroyed, it is reset and - // the key material is zeroised using the STL fill - // function. - ~HMAC(void); + /// When an HMAC context is destroyed, it is reset and + /// the key material is zeroised using the STL `fill` + /// function. + ~HMAC(); private: uint8_t hstate; SHA256 ctx; uint8_t k[HMAC_KEY_LENGTH]; - uint8_t buf[SHA256_HASH_SIZE]; + uint8_t buf[SHA256_HASH_SIZE]; - EMSHA_RESULT reset(); - inline EMSHA_RESULT - finalResult(uint8_t *d); + EMSHAResult reset(); + inline EMSHAResult finalResult(uint8_t *d); }; -// compute_hmac performs a single-pass HMAC computation over -// a message. -// -// Inputs: -// k: a byte buffer containing the HMAC key. -// -// kl: the length of the HMAC key. -// -// m: the message data over which the HMAC is to be computed. -// -// ml: the length of the message. -// -// d: a byte buffer that will be used to store the resulting -// HMAC. It should be SHA256_HASH_SIZE bytes in size. -// -// Outputs: -// This function handles setting up the HMAC context with -// the given key, calling update with the message data, and -// then calling finalize to place the result in the output -// buffer. Any of the faults that can occur in these functions -// can be returned here, or EMSHA_ROK if the HMAC was -// successfully computed. -EMSHA_RESULT -ComputeHMAC(const uint8_t *k, uint32_t kl, - const uint8_t *m, uint32_t ml, + +/// \brief Perform a single-pass HMAC computation over a message. +/// +/// \param k A byte buffer containing the HMAC key. +/// \param kl The length of the HMAC key. +/// \param m The message data over which the HMAC is to be computed. +/// \param ml The length of the message. +/// \param d Byte buffer that will be used to store the resulting +/// HMAC. It should be emsha::SHA256_HASH_SIZE bytes in size. +/// \return An ::EMSHAResult describing the result of the HMAC operation. +EMSHAResult +ComputeHMAC(const uint8_t *k, const uint32_t kl, + const uint8_t *m, const uint32_t ml, uint8_t *d); + + } // end of namespace emsha diff --git a/emsha/sha256.h b/emsha/sha256.h index e9e796d..b781909 100644 --- a/emsha/sha256.h +++ b/emsha/sha256.h @@ -34,184 +34,160 @@ #include #include +#include namespace emsha { -// SHA256_MB_SIZE is the size of a message block. +/// SHA256_MB_SIZE is the size of a message block. const uint32_t SHA256_MB_SIZE = 64; class SHA256 : Hash { public: - // A SHA256 context does not need any special - // construction. It can be declared and - // immediately start being used. + /// \brief A SHA256 context does not need any special + /// construction. + /// + /// It can be declared and immediately start being used. SHA256(); - // The SHA256 destructor will clear out its internal - // message buffer; all the members are local and - // not resource handles, so cleanup is minimal. + /// The SHA256 destructor will clear out its internal + /// message buffer; all the members are local and + /// not resource handles, so cleanup is minimal. ~SHA256(); - // reset clears the internal state of the SHA256 - // context and returns it to its initial state. - // It should always return EMSHA_ROK. - EMSHA_RESULT Reset() override; + /// \brief Clear the internal state of the SHA256 context, + /// returning it to its initial state. + /// + /// \return This should always return EMSHAResult::OK. + EMSHAResult Reset() override; - // update writes data into the context. While - // there is an upper limit on the size of data - // that SHA-256 can operate on, this package is - // designed for small systems that will not - // approach that level of data (which is on the - // order of 2 exabytes), so it is not thought - // to be a concern. - // - // Inputs: - // m: a byte array containing the message to - // be written. It must not be NULL (unless - // the message length is zero). - // - // ml: the message length, in bytes. - // - // Outputs: - // EMSHA_NULLPTR is returned if m is NULL - // and ml is nonzero. - // - // EMSHA_INVALID_STATE is returned if the - // update is called after a call to - // finalize. - // - // SHA256_INPUT_TOO_LONG is returned if too - // much data has been written to the - // context. - // - // EMSHA_ROK is returned if the data was - // successfully added to the SHA-256 - // context. - // - EMSHA_RESULT Update(const uint8_t *m, uint32_t ml) override; + /// \brief Writes data into the SHA256. + /// + /// While there is an upper limit on the size of data that + /// SHA-256 can operate on, this package is designed for small + /// systems that will not approach that level of data (which is + /// on the order of 2 exabytes), so it is not thought to be a + /// concern. + /// + /// \param message A byte array containing the message to be + /// written. It must not be NULL (unless the + /// message length is zero). + /// \param messageLength The message length, in bytes. + /// \return An ::EMSHAResult describing the result of the + /// operation. + /// + /// - EMSHAResult::NullPointer is returned if m is a + /// nullptr and ml is nonzero. + /// - EMSHAResult::InvalidState is returned if the + /// update is called after a call to finalize. + /// - EMSHAResult::InputTooLong is returned if too much + /// data has been written to the context. + /// - EMSHAResult::OK is returned if the data was + /// successfully added to the SHA-256 context. + EMSHAResult Update(const std::uint8_t *message, std::uint32_t messageLength) override; - // Finalise completes the digest. Once this - // method is called, the context cannot be - // updated unless the context is reset. - // - // Inputs: - // d: a byte buffer that must be at least - // SHA256.size() in length. - // - // Outputs: - // EMSHA_NULLPTR is returned if d is the - // null pointer. - // - // EMSHA_INVALID_STATE is returned if the - // SHA-256 context is in an invalid state, - // such as if there were errors in previous - // updates. - // - // EMSHA_ROK is returned if the context was - // successfully finalised and the digest - // copied to d. - // - EMSHA_RESULT Finalise(uint8_t *d) override; + /// \brief Complete the digest. + /// + /// Once this method is called, the context cannot be updated + /// unless the context is reset. + /// + /// \param digest byte buffer that must be at least + /// SHA256.size() in length. + /// \return An ::EMSHAResult describing the result of the + /// operation. + /// + /// - EMSHAResult::NullPointer is returned if a nullptr + /// is passed in. + /// - EMSHAResult::InvalidState is returned if the + /// SHA-256 context is in an invalid state, such as + /// if there were errors in previous updates. + /// - EMSHAResult::OK is returned if the context was + /// successfully finalised and the digest copied to + /// digest. + EMSHAResult Finalise(std::uint8_t *digest) override; - // result copies the result from the SHA-256 - // context into the buffer pointed to by d, - // running Finalise if needed. Once called, - // the context cannot be updated until the - // context is reset. - // - // Inputs: - // d: a byte buffer that must be at least - // SHA256.size() in length. - // - // Outputs: - // EMSHA_NULLPTR is returned if d is the - // null pointer. - // - // EMSHA_INVALID_STATE is returned if the - // SHA-256 context is in an invalid state, - // such as if there were errors in previous - // updates. - // - // EMSHA_ROK is returned if the context was - // successfully finalised and the digest - // copied to d. - // - EMSHA_RESULT Result(uint8_t *d) override; + /// \brief Copy the result from the SHA-256 + /// context into the buffer pointed to by d, + /// running #Finalise if needed. Once called, + /// the context cannot be updated until the + /// context is reset. + /// + /// \param digest A byte buffer that must be at least + /// SHA256.size() in length. + /// \return An ::EMSHAResult describing the result of the + /// operation. + /// + /// - EMSHAResult::NullPointer is returned if a nullptr + /// is passed in. + /// - EMSHAResult::InvalidState is returned if the + /// SHA-256 context is in an invalid state, such as + /// if there were errors in previous updates. + /// - EMSHAResult::OK is returned if the context was + /// successfully finalised and the digest copied to + /// digest. + EMSHAResult Result(std::uint8_t *digest) override; - // size returns the output size of SHA256, e.g. - // the size that the buffers passed to finalize - // and result should be. - // - // Outputs: - // a uint32_t representing the expected size - // of buffers passed to result and finalize. + /// \brief Returns the output size of SHA-256. + /// + /// The buffers passed to #Update and #Finalise should be at + /// least this size. + /// + /// \return The expected size of buffers passed to result and + /// finalize. std::uint32_t Size() override; private: - // mlen stores the current message length. - uint64_t mlen; - - // The intermediate hash is 8x 32-bit blocks. - uint32_t i_hash[8]; + uint64_t mlen; // Current message length. + uint32_t i_hash[8]; // The intermediate hash is 8x 32-bit blocks. // hStatus is the hash status, and hComplete indicates // whether the hash has been finalised. - EMSHA_RESULT hStatus; - uint8_t hComplete; + EMSHAResult hStatus; + uint8_t hComplete; // mb is the message block, and mbi is the message // block index. uint8_t mbi; - uint8_t mb[SHA256_MB_SIZE]; + std::array mb; - inline EMSHA_RESULT addLength(const uint32_t); - inline void updateMessageBlock(void); - inline void padMessage(uint8_t pc); - EMSHA_RESULT reset(); + inline EMSHAResult addLength(const uint32_t); + inline void updateMessageBlock(void); + inline void padMessage(uint8_t pc); + uint32_t chunkToUint32(uint32_t offset); + uint32_t uint32ToChunk(uint32_t offset); + EMSHAResult reset(); }; // end class SHA256 -// sha256Digest performs a single pass hashing of the message -// passed in. -// -// Inputs: -// m: byte buffer containing the message to hash. -// -// ml: the length of m. -// -// d: byte buffer that will be used to store the resulting -// hash; it should have at least emsha::SHA256_HASH_SIZE -// bytes available. -// -// Outputs: -// This function handles setting up a SHA256 context, calling -// update using the message data, and then calling finalize. Any -// of the errors that can occur in those functions can be -// returned here, or EMSHA_ROK if the digest was computed -// successfully. -// -EMSHA_RESULT -sha256Digest(const uint8_t *m, uint32_t ml, uint8_t *d); -// sha256SelfTest runs through two test cases to ensure that the -// SHA-256 functions are working correctly. -// -// Outputs: -// EMSHA_ROK is returned if the self tests pass. -// -// EMSHA_SELFTEST_DISABLED is returned if the self tests -// have been disabled (e.g., libemsha was compiled with the -// EMSHA_NO_SELFTEST #define). -// -// If a fault occurred inside the SHA-256 code, the error -// code from one of the update, finalize, result, or reset -// methods is returned. -// -// If the fault is that the output does not match the test -// vector, EMSHA_TEST_FAILURE is returned. -EMSHA_RESULT -sha256SelfTest(void); +/// \brief SHA256Digest performs a single pass hashing of the message +/// passed in. +/// +/// \param m Byte buffer containing the message to hash. +/// \param ml The length of m. +/// \param d Byte buffer that will be used to store the resulting hash; +/// it should have at least emsha::SHA256_HASH_SIZE bytes +/// available. +/// \return An ::EMSHAResult describing the result of the operation. +EMSHAResult SHA256Digest(const uint8_t *m, uint32_t ml, uint8_t *d); + +/// \brief SHA256SelfTest runs through two test cases to ensure that the +/// SHA-256 functions are working correctly. +/// +/// \return The result of the self-test. +/// +/// - EMSHAResult::OK is returned if the self tests pass. +/// - EMSHAResult::SelfTestDisabled is returned if the self +/// tests have been disabled (e.g., libemsha was compiled +/// with the EMSHA_NO_SELFTEST #define). +/// - If a fault occurred inside the SHA-256 code, the error +/// code from one of the update, finalize, result, or reset +/// methods is returned. +/// - If the fault is that the output does not match the test +/// vector, EMSHAResult::TestFailure is returned. +EMSHAResult SHA256SelfTest(); + + } // end of namespace emsha diff --git a/hmac.cc b/hmac.cc index 7e8b347..c672e34 100644 --- a/hmac.cc +++ b/hmac.cc @@ -63,11 +63,12 @@ static constexpr uint8_t opad = 0x5c; HMAC::HMAC(const uint8_t *ik, uint32_t ikl) : hstate(HMAC_INIT), k{0}, buf{0} { - - std::fill(this->k, this->k + emsha::HMAC_KEY_LENGTH, 0); + std::fill(this->k, this->k+HMAC_KEY_LENGTH, 0); if (ikl < HMAC_KEY_LENGTH) { - std::copy(ik, ik + ikl, this->k); + for (uint32_t i = 0; i < ikl; i++) { + this->k[i] = ik[i]; + } while (ikl < HMAC_KEY_LENGTH) { this->k[ikl++] = 0; } @@ -76,7 +77,9 @@ HMAC::HMAC(const uint8_t *ik, uint32_t ikl) this->ctx.Result(this->k); this->ctx.Reset(); } else { - std::copy(ik, ik + ikl, this->k); + for (uint32_t i = 0; i < ikl; i++) { + this->k[i] = ik[i]; + } } this->reset(); @@ -93,17 +96,17 @@ HMAC::~HMAC() } -EMSHA_RESULT +EMSHAResult HMAC::Reset() { return this->reset(); } -EMSHA_RESULT +EMSHAResult HMAC::reset() { - EMSHA_RESULT res; + EMSHAResult res; // Following a reset, both SHA-256 contexts and result buffer should be // zero'd out for a clean slate. The HMAC state should be reset @@ -119,7 +122,7 @@ HMAC::reset() } res = this->ctx.Update(key, HMAC_KEY_LENGTH); - if (EMSHA_ROK != res) { + if (EMSHAResult::OK != res) { this->hstate = HMAC_INVALID; return res; } @@ -128,55 +131,56 @@ HMAC::reset() std::fill(key, key + HMAC_KEY_LENGTH, 0); this->hstate = HMAC_IPAD; - return EMSHA_ROK; + return EMSHAResult::OK; } -EMSHA_RESULT -HMAC::Update(const uint8_t *m, uint32_t ml) +EMSHAResult +HMAC::Update(const std::uint8_t *message, std::uint32_t messageLength) { - EMSHA_RESULT res; - SHA256 &hctx = this->ctx; + EMSHAResult res; + SHA256 &hctx = this->ctx; - EMSHA_CHECK(m != nullptr, EMSHA_NULLPTR); - EMSHA_CHECK(HMAC_IPAD == this->hstate, EMSHA_INVALID_STATE); + EMSHA_CHECK(message != nullptr, EMSHAResult::NullPointer); + EMSHA_CHECK(HMAC_IPAD == this->hstate, EMSHAResult::InvalidState); // Write the message to the SHA-256 context. - res = hctx.Update(m, ml); - if (EMSHA_ROK != res) { + res = hctx.Update(message, messageLength); + if (EMSHAResult::OK != res) { this->hstate = HMAC_INVALID; return res; } assert(HMAC_IPAD == this->hstate); - return EMSHA_ROK; + return EMSHAResult::OK; } -inline EMSHA_RESULT +inline EMSHAResult HMAC::finalResult(uint8_t *d) { if (nullptr == d) { - return EMSHA_NULLPTR; + return EMSHAResult::NullPointer; } // If the HMAC has already been finalised, skip straight to // copying the result. - if (HMAC_FIN == this->hstate) { - std::copy(this->buf, this->buf + SHA256_HASH_SIZE, d); - return EMSHA_ROK; + if (this->hstate == HMAC_FIN) { + std::copy(this->buf, this->buf+SHA256_HASH_SIZE, d); + + return EMSHAResult::OK; } - EMSHA_CHECK(HMAC_IPAD == this->hstate, EMSHA_INVALID_STATE); + EMSHA_CHECK(HMAC_IPAD == this->hstate, EMSHAResult::InvalidState); - EMSHA_RESULT res; + EMSHAResult res; // Use the result buffer as an intermediate buffer to store the result // of the inner hash. res = this->ctx.Result(this->buf); - if (EMSHA_ROK != res) { + if (EMSHAResult::OK != res) { this->hstate = HMAC_INVALID; - return EMSHA_INVALID_STATE; + return EMSHAResult::InvalidState; } assert(HMAC_IPAD == this->hstate); @@ -192,7 +196,7 @@ HMAC::finalResult(uint8_t *d) } res = this->ctx.Update(key, HMAC_KEY_LENGTH); - if (EMSHA_ROK != res) { + if (EMSHAResult::OK != res) { this->hstate = HMAC_INVALID; return res; } @@ -203,14 +207,14 @@ HMAC::finalResult(uint8_t *d) // Write the inner hash result into the outer hash. res = this->ctx.Update(this->buf, SHA256_HASH_SIZE); - if (EMSHA_ROK != res) { + if (EMSHAResult::OK != res) { this->hstate = HMAC_INVALID; return res; } // Write the outer hash result into the working buffer. res = this->ctx.Finalise(this->buf); - if (EMSHA_ROK != res) { + if (EMSHAResult::OK != res) { this->hstate = HMAC_INVALID; return res; } @@ -218,21 +222,21 @@ HMAC::finalResult(uint8_t *d) std::copy(this->buf, this->buf + SHA256_HASH_SIZE, d); this->hstate = HMAC_FIN; - return EMSHA_ROK; + return EMSHAResult::OK; } -EMSHA_RESULT -HMAC::Finalise(uint8_t *d) +EMSHAResult +HMAC::Finalise(std::uint8_t *digest) { - return this->finalResult(d); + return this->finalResult(digest); } -EMSHA_RESULT -HMAC::Result(uint8_t *d) +EMSHAResult +HMAC::Result(std::uint8_t *digest) { - return this->finalResult(d); + return this->finalResult(digest); } @@ -243,21 +247,17 @@ HMAC::Size() } -EMSHA_RESULT -ComputeHMAC(const uint8_t *k, uint32_t kl, const uint8_t *m, uint32_t ml, +EMSHAResult +ComputeHMAC(const uint8_t *k, const uint32_t kl, + const uint8_t *m, const uint32_t ml, uint8_t *d) { - EMSHA_RESULT res; - HMAC h(k, kl); + EMSHAResult res; + HMAC h(k, kl); res = h.Update(m, ml); - if (EMSHA_ROK != res) { - return res; - } - - res = h.Result(d); - if (EMSHA_ROK != res) { - return res; + if (res == EMSHAResult::OK) { + res = h.Result(d); } return res; diff --git a/sha256.cc b/sha256.cc index 5612dab..4381efe 100644 --- a/sha256.cc +++ b/sha256.cc @@ -69,13 +69,13 @@ static constexpr uint32_t emsha256H0[] = { }; -EMSHA_RESULT -sha256Digest(const uint8_t *m, uint32_t ml, uint8_t *d) +EMSHAResult +SHA256Digest(const uint8_t *m, uint32_t ml, uint8_t *d) { - SHA256 h; - EMSHA_RESULT ret; + SHA256 h; + EMSHAResult ret = EMSHAResult::Unknown; - if (EMSHA_ROK != (ret = h.Update(m, ml))) { + if (EMSHAResult::OK != (ret = h.Update(m, ml))) { return ret; } @@ -92,33 +92,35 @@ SHA256::SHA256() SHA256::~SHA256() { - memset(this->mb, 0, SHA256_MB_SIZE); + for (auto i = static_cast(0); i < SHA256_MB_SIZE; i++) { + this->mb[i] = static_cast(0); + } } -EMSHA_RESULT +EMSHAResult SHA256::addLength(const uint32_t l) { - uint32_t tmp = this->mlen + l; + EMSHAResult res = EMSHAResult::InputTooLong;; - if (tmp < this->mlen) { - return SHA256_INPUT_TOO_LONG; + uint32_t const tmp = static_cast(this->mlen) + l; + if (tmp >= this->mlen) { + this->mlen = tmp; + assert(this->mlen > 0); + res = EMSHAResult::OK; } - this->mlen = tmp; - assert(this->mlen > 0); - - return EMSHA_ROK; + return res; } -EMSHA_RESULT +EMSHAResult SHA256::Reset() { return this->reset(); } -EMSHA_RESULT +EMSHAResult SHA256::reset() { // The message block is set to the initial hash vector. @@ -132,29 +134,47 @@ SHA256::reset() this->i_hash[7] = emsha256H0[7]; this->mbi = 0; - this->hStatus = EMSHA_ROK; + this->hStatus = EMSHAResult::OK; this->hComplete = 0; this->mlen = 0; - memset(this->mb, 0, SHA256_MB_SIZE); + + std::fill(this->mb.begin(), this->mb.end(), 0); return this->hStatus; } -// Read 32 bits from the byte buffer chunk as an unsigned 32-bit integer. -static uint32_t -chunkToUint32(const uint8_t *chunk) +uint32_t +SHA256::chunkToUint32(uint32_t offset) { - return ((*chunk) << 24) | - ((*(chunk + 1)) << 16) | - ((*(chunk + 2)) << 8) | - (*(chunk + 3)); + uint32_t chunk = 0; + + for (uint32_t i = offset; i < offset+4; i++) { + chunk <<= 8; + chunk += static_cast(this->mb[i]); + } + + return chunk; +} + + +uint32_t +SHA256::uint32ToChunk(uint32_t offset) +{ + uint32_t chunk = 0; + + for (uint32_t i = offset; i < offset+4; i++) { + chunk <<= 8; + chunk += static_cast(this->mb[i]); + } + + return chunk; + } -// Copy an unsigned 32-bit integer into the start of the byte buffer chunk. static void -uint32ToChunk(uint32_t x, uint8_t *chunk) +uint32ToChunkInPlace(uint32_t x, uint8_t *chunk) { chunk[0] = (x & 0xff000000) >> 24; chunk[1] = (x & 0x00ff0000) >> 16; @@ -180,7 +200,7 @@ SHA256::updateMessageBlock() uint32_t h = 0; while (i < 16) { - w[i++] = chunkToUint32(this->mb + chunk); + w[i++] = this->chunkToUint32(chunk); chunk += 4; } this->mbi = 0; @@ -225,36 +245,36 @@ SHA256::updateMessageBlock() } -EMSHA_RESULT -SHA256::Update(const uint8_t *m, uint32_t ml) +EMSHAResult +SHA256::Update(const std::uint8_t *message, std::uint32_t messageLength) { // Checking invariants: // If the message length is zero, there's nothing to be done. - if (0 == ml) { return EMSHA_ROK; } + if (0 == messageLength) { return EMSHAResult::OK; } // The message passed in cannot be the null pointer if the // message length is greater than 0. - if (nullptr == m) { return EMSHA_NULLPTR; } + if (message == nullptr) { return EMSHAResult::NullPointer; } // If the SHA256 object is in a bad state, don't proceed. - if (EMSHA_ROK != this->hStatus) { return this->hStatus; } + if (this->hStatus != EMSHAResult::OK) { return this->hStatus; } // If the hash has been finalised, don't proceed. - if (0 != this->hComplete) { return EMSHA_INVALID_STATE; } + if (this->hComplete != static_cast(0)) { return EMSHAResult::InvalidState; } // Invariants satisfied by here. - for (uint32_t i = 0; i < ml; i++) { - this->mb[this->mbi] = *(m + i); + for (uint32_t i = 0; i < messageLength; i++) { + this->mb[this->mbi] = *(message + i); mbi++; - if (EMSHA_ROK == this->addLength(8)) { + if (EMSHAResult::OK == this->addLength(8)) { if (SHA256_MB_SIZE == this->mbi) { this->updateMessageBlock(); // Assumption: following the message block // write, the context should still be in a good // state. - assert(EMSHA_ROK == this->hStatus); + assert(EMSHAResult::OK == this->hStatus); } } } @@ -267,7 +287,7 @@ inline void SHA256::padMessage(uint8_t pc) { // Assumption: the context is not in a corrupted state. - assert(EMSHA_ROK == this->hStatus); + assert(EMSHAResult::OK == this->hStatus); if (this->mbi < (SHA256_MB_SIZE - 8)) { this->mb[this->mbi++] = pc; @@ -290,7 +310,7 @@ SHA256::padMessage(uint8_t pc) // Assumption: updating the message block has not left the // context in a corrupted state. - assert(EMSHA_ROK == this->hStatus); + assert(EMSHAResult::OK == this->hStatus); } while (this->mbi < (SHA256_MB_SIZE - 8)) { @@ -320,76 +340,76 @@ SHA256::padMessage(uint8_t pc) // Assumption: updating the message block has not left the context in a // corrupted state. - assert(EMSHA_ROK == this->hStatus); + assert(EMSHAResult::OK == this->hStatus); } -EMSHA_RESULT -SHA256::Finalise(uint8_t *d) +EMSHAResult +SHA256::Finalise(std::uint8_t *digest) { // Check invariants. // The digest cannot be a null pointer; this library allocates // no memory of its own. - if (nullptr == d) { return EMSHA_NULLPTR; } + if (nullptr == digest) { return EMSHAResult::NullPointer; } // If the SHA256 object is in a bad state, don't proceed. - if (EMSHA_ROK != this->hStatus) { return this->hStatus; } + if (EMSHAResult::OK != this->hStatus) { return this->hStatus; } // If the hash has been finalised, don't proceed. - if (0 != this->hComplete) { return EMSHA_INVALID_STATE; } + if (0 != this->hComplete) { return EMSHAResult::InvalidState; } // Invariants satisfied by here. this->padMessage(0x80); // Assumption: padding the message block has not left the context in a // corrupted state. - assert(EMSHA_ROK == this->hStatus); - std::fill(this->mb, this->mb + SHA256_MB_SIZE, 0); + assert(EMSHAResult::OK == this->hStatus); + std::fill(this->mb.begin(), this->mb.end(), 0); this->hComplete = 1; this->mlen = 0; - uint32ToChunk(this->i_hash[0], d); - uint32ToChunk(this->i_hash[1], d + 4); - uint32ToChunk(this->i_hash[2], d + 8); - uint32ToChunk(this->i_hash[3], d + 12); - uint32ToChunk(this->i_hash[4], d + 16); - uint32ToChunk(this->i_hash[5], d + 20); - uint32ToChunk(this->i_hash[6], d + 24); - uint32ToChunk(this->i_hash[7], d + 28); + uint32ToChunkInPlace(this->i_hash[0], digest); + uint32ToChunkInPlace(this->i_hash[1], digest + 4); + uint32ToChunkInPlace(this->i_hash[2], digest + 8); + uint32ToChunkInPlace(this->i_hash[3], digest + 12); + uint32ToChunkInPlace(this->i_hash[4], digest + 16); + uint32ToChunkInPlace(this->i_hash[5], digest + 20); + uint32ToChunkInPlace(this->i_hash[6], digest + 24); + uint32ToChunkInPlace(this->i_hash[7], digest + 28); - return EMSHA_ROK; + return EMSHAResult::OK; } -EMSHA_RESULT -SHA256::Result(uint8_t *d) +EMSHAResult +SHA256::Result(std::uint8_t *digest) { // Check invariants. // The digest cannot be a null pointer; this library allocates // no memory of its own. - if (nullptr == d) { return EMSHA_NULLPTR; } + if (nullptr == digest) { return EMSHAResult::NullPointer; } // If the SHA256 object is in a bad state, don't proceed. - if (EMSHA_ROK != this->hStatus) { return this->hStatus; } + if (EMSHAResult::OK != this->hStatus) { return this->hStatus; } // Invariants satisfied by here. if (this->hComplete == 0U) { - return this->Finalise(d); + return this->Finalise(digest); } - uint32ToChunk(this->i_hash[0], d); - uint32ToChunk(this->i_hash[1], d + 4); - uint32ToChunk(this->i_hash[2], d + 8); - uint32ToChunk(this->i_hash[3], d + 12); - uint32ToChunk(this->i_hash[4], d + 16); - uint32ToChunk(this->i_hash[5], d + 20); - uint32ToChunk(this->i_hash[6], d + 24); - uint32ToChunk(this->i_hash[7], d + 28); + uint32ToChunkInPlace(this->i_hash[0], digest); + uint32ToChunkInPlace(this->i_hash[1], digest + 4); + uint32ToChunkInPlace(this->i_hash[2], digest + 8); + uint32ToChunkInPlace(this->i_hash[3], digest + 12); + uint32ToChunkInPlace(this->i_hash[4], digest + 16); + uint32ToChunkInPlace(this->i_hash[5], digest + 20); + uint32ToChunkInPlace(this->i_hash[6], digest + 24); + uint32ToChunkInPlace(this->i_hash[7], digest + 28); - return EMSHA_ROK; + return EMSHAResult::OK; } @@ -426,22 +446,22 @@ static const uint8_t helloWorld[] = { constexpr uint32_t EMSHA_SELF_TEST_ITERS = 4; -static EMSHA_RESULT +static EMSHAResult runTest(const uint8_t *input, uint32_t input_len, const uint8_t *expected) { - uint8_t hexString[65]{0}; - uint8_t d[SHA256_HASH_SIZE]{0}; - emsha::SHA256 ctx; - emsha::EMSHA_RESULT res; + uint8_t hexString[65]{0}; + uint8_t d[SHA256_HASH_SIZE]{0}; + emsha::SHA256 ctx; + emsha::EMSHAResult res; res = ctx.Update(input, input_len); - if (EMSHA_ROK != res) { + if (EMSHAResult::OK != res) { return res; } for (uint32_t n = 0; n < EMSHA_SELF_TEST_ITERS; n++) { res = ctx.Result(d); - if (EMSHA_ROK != res) { + if (EMSHAResult::OK != res) { return res; } @@ -451,24 +471,24 @@ runTest(const uint8_t *input, uint32_t input_len, const uint8_t *expected) std::cerr << "[!] have: " << hexString << "\n"; HexString(hexString, const_cast(helloWorld), 32); std::cerr << "[!] want: " << hexString << "\n"; - return EMSHA_TEST_FAILURE; + return EMSHAResult::TestFailure; } } } - return EMSHA_ROK; + return EMSHAResult::OK; } -EMSHA_RESULT -sha256SelfTest() +EMSHAResult +SHA256SelfTest() { - EMSHA_RESULT res; + EMSHAResult res; res = runTest(reinterpret_cast(""), 0, emptyVector); - if (EMSHA_ROK == res) { + if (EMSHAResult::OK == res) { res = runTest(reinterpret_cast("hello, world"), 12, helloWorld); - if (res != EMSHA_ROK) { + if (res != EMSHAResult::OK) { std::cerr << "[!] failed on hello, world.\n"; } } else { @@ -480,10 +500,10 @@ sha256SelfTest() #else // #ifdef EMSHA_NO_SELFTEST -EMSHA_RESULT +EMSHAResult sha256_self_test() { - return EMSHA_SELFTEST_DISABLED; + return EMSHAResult::SelfTestDisabled; } diff --git a/test_emsha.cc b/test_emsha.cc index 34b09eb..34f4dda 100644 --- a/test_emsha.cc +++ b/test_emsha.cc @@ -23,8 +23,8 @@ */ +#include #include - #include #include "test_utils.h" @@ -33,6 +33,10 @@ using namespace std; +// how many test runs to benchmark hex strings? +static constexpr auto testIterations = 32768; + + #ifndef EMSHA_NO_HEXSTRING static void hexStringTest() @@ -49,19 +53,11 @@ hexStringTest() emsha::HexString(out, buf, emsha::SHA256_HASH_SIZE); string const outs(reinterpret_cast(out)); if (outs != expected) { - cerr << "FAILED: HexString" << endl; - cerr << "\twanted: " << expected << endl; - cerr << "\thave: " << out << endl; + cerr << "FAILED: HexString\n"; + cerr << "\twanted: " << expected << "\n"; + cerr << "\thave: " << out << "\n"; exit(1); } - - cout << "PASSED: HexString "; -#ifdef EMSHA_NO_HEXLUT - cout << "(small LUT)"; -#else // #ifdef EMSHA_NO_HEXLUT - cout << "(large LUT)"; -#endif // #ifdef EMSHA_NO_HEXLUT - cout << endl; } #endif // #ifndef EMSHA_NO_HEXSTRING @@ -84,9 +80,9 @@ hashEqualTest() cerr << "FAILED: HashEqual\n"; cerr << "\tHashEqual should have succeeded comparing a and b.\n"; DumpHexString(s, a, emsha::SHA256_HASH_SIZE); - cerr << "\ta <- " << s << std::endl; + cerr << "\ta <- " << s << "\n"; DumpHexString(s, b, emsha::SHA256_HASH_SIZE); - cerr << "\tb <- " << s << std::endl; + cerr << "\tb <- " << s << "\n"; exit(1); } @@ -100,9 +96,9 @@ hashEqualTest() cerr << "FAILED: HashEqual\n"; cerr << "\tHashEqual should not have succeeded comparing a and b.\n"; DumpHexString(s, a, emsha::SHA256_HASH_SIZE); - cerr << "\ta <- " << s << std::endl; + cerr << "\ta <- " << s << "\n"; DumpHexString(s, b, emsha::SHA256_HASH_SIZE); - cerr << "\tb <- " << s << std::endl; + cerr << "\tb <- " << s << "\n"; exit(1); } @@ -126,17 +122,33 @@ hashEqualTest() cerr << "\tb <- " << s << std::endl; exit(1); } - - - cout << "PASSED: HashEqual\n"; } int main() { + auto start = std::chrono::steady_clock::now(); + std::string testLabel; + + for (auto i = 0; i < testIterations; i++) { #ifndef EMSHA_NO_HEXSTRING - hexStringTest(); +#ifndef EMSHA_NO_HEXLUT + testLabel = "(large LUT) "; #endif - hashEqualTest(); + hexStringTest(); +#endif + hashEqualTest(); + } + + auto end = std::chrono::steady_clock::now(); + auto delta = (end - start); + + std::cout << "Passed HexString " << testLabel << "tests.\n"; + std::cout << "Total time: " + << std::chrono::duration(delta).count() + << " ms\n"; + std::cout << "Average over " << testIterations << " tests: " + << std::chrono::duration(delta).count() / testIterations + << " ns\n"; } diff --git a/test_mem.cc b/test_mem.cc index 70edd39..047f0c6 100644 --- a/test_mem.cc +++ b/test_mem.cc @@ -41,7 +41,7 @@ // Number of test iterations. -static constexpr std::uint32_t ITERS = 8192; +static constexpr std::uint32_t ITERS = 32768; // The key used for HMAC. static constexpr std::uint8_t k[] = { @@ -111,13 +111,13 @@ static void iterateSHA() { emsha::SHA256 ctx; - int cmp = 0; - emsha::EMSHA_RESULT res; + int cmp = 0; + emsha::EMSHAResult res; res = ctx.Update(m, sizeof(m)); - assert(emsha::EMSHA_ROK == res); + assert(emsha::EMSHAResult::OK == res); res = ctx.Result(dig); - assert(emsha::EMSHA_ROK == res); + assert(emsha::EMSHAResult::OK == res); cmp = std::memcmp(dig, d, emsha::SHA256_HASH_SIZE); assert(0 == cmp); @@ -128,13 +128,13 @@ static void iterateHMAC() { emsha::HMAC ctx(k, kl); - int cmp = 0; - emsha::EMSHA_RESULT res; + int cmp = 0; + emsha::EMSHAResult res; res = ctx.Update(m, sizeof(m)); - assert(emsha::EMSHA_ROK == res); + assert(emsha::EMSHAResult::OK == res); res = ctx.Result(dig); - assert(emsha::EMSHA_ROK == res); + assert(emsha::EMSHAResult::OK == res); cmp = std::memcmp(dig, t, emsha::SHA256_HASH_SIZE); assert(0 == cmp); @@ -146,7 +146,7 @@ iterateSHASP() { int cmp = 0; - assert(emsha::EMSHA_ROK == emsha::sha256Digest(m, sizeof(m), dig)); + assert(emsha::EMSHAResult::OK == emsha::SHA256Digest(m, sizeof(m), dig)); cmp = std::memcmp(dig, d, emsha::SHA256_HASH_SIZE); assert(0 == cmp); } @@ -155,11 +155,11 @@ iterateSHASP() static void iterateHMACSP() { - int cmp = 0; - emsha::EMSHA_RESULT res; + int cmp = 0; + emsha::EMSHAResult res; res = emsha::ComputeHMAC(k, kl, m, sizeof(m), dig); - assert(emsha::EMSHA_ROK == res); + assert(emsha::EMSHAResult::OK == res); cmp = std::memcmp(dig, t, emsha::SHA256_HASH_SIZE); assert(0 == cmp); diff --git a/test_sha256.cc b/test_sha256.cc index 5436248..badfffb 100644 --- a/test_sha256.cc +++ b/test_sha256.cc @@ -76,18 +76,34 @@ main() #ifdef EMSHA_NO_SELFTEST cout << "[NOTICE] internal self-tests have been disabled.\n"; #else - auto selfTestStatus = emsha::sha256SelfTest(); + auto selfTestStatus = emsha::SHA256SelfTest(); switch (selfTestStatus) { - case emsha::EMSHA_ROK: + case emsha::EMSHAResult::OK: cout << "PASSED: SHA-256 self test\n"; break; - case emsha::EMSHA_TEST_FAILURE: - cout << "FAILED: SHA-256 self test (test failure)\n"; + case emsha::EMSHAResult::TestFailure: + cout << "FAILED: SHA-256 self-test\n"; + break; + case emsha::EMSHAResult::Unknown: + cout << "FAILED: SHA-256 self test (fault: Unknown)\n"; + break; + case emsha::EMSHAResult::NullPointer: + cout << "FAILED: SHA-256 self test (fault: NullPointer)\n"; + break; + case emsha::EMSHAResult::InvalidState: + cout << "FAILED: SHA-256 self test (fault: InvalidState)\n"; + break; + case emsha::EMSHAResult::InputTooLong: + cout << "FAILED: SHA-256 self test (fault: InputTooLong)\n"; + break; + case emsha::EMSHAResult::SelfTestDisabled: + cout << "FAILED: SHA-256 self test (fault: SelfTestDisabled)\n"; break; default: - cout << "FAILED: SHA-256 self test (fault " << selfTestStatus << ")\n"; + cout << "FAILED: SHA-256 self test (fault: internal system failure)\n"; + abort(); } - assert(selfTestStatus == emsha::EMSHA_ROK); + assert(selfTestStatus == emsha::EMSHAResult::OK); #endif diff --git a/test_utils.cc b/test_utils.cc index 39dd2e6..4f1e35d 100644 --- a/test_utils.cc +++ b/test_utils.cc @@ -53,28 +53,28 @@ DumpHexString(std::string& hs, uint8_t *s, uint32_t sl) } -emsha::EMSHA_RESULT -runHMACTest(const struct hmacTest test, const string& label) +emsha::EMSHAResult +runHMACTest(const struct hmacTest& test, const string& label) { - emsha::HMAC h(test.key, test.keylen); - emsha::EMSHA_RESULT res; - uint8_t dig[emsha::SHA256_HASH_SIZE]; + emsha::HMAC h(test.key, test.keylen); + emsha::EMSHAResult res; + uint8_t dig[emsha::SHA256_HASH_SIZE]; string hs; res = h.Update((uint8_t *)test.input.c_str(), test.input.size()); - if (emsha::EMSHA_ROK != res) { + if (emsha::EMSHAResult::OK != res) { goto exit; } for (uint32_t n = 0; n < RESULT_ITERATIONS; n++) { res = h.Result(dig); - if (emsha::EMSHA_ROK != res) { + if (emsha::EMSHAResult::OK != res) { goto exit; } DumpHexString(hs, dig, emsha::SHA256_HASH_SIZE); if (hs != test.output) { - res = emsha::EMSHA_TEST_FAILURE; + res = emsha::EMSHAResult::TestFailure; goto exit; } memset(dig, 0, emsha::SHA256_HASH_SIZE); @@ -84,19 +84,19 @@ runHMACTest(const struct hmacTest test, const string& label) h.Reset(); res = h.Update((uint8_t *)test.input.c_str(), test.input.size()); - if (emsha::EMSHA_ROK != res) { + if (emsha::EMSHAResult::OK != res) { goto exit; } for (uint32_t n = 0; n < RESULT_ITERATIONS; n++) { res = h.Result(dig); - if (emsha::EMSHA_ROK != res) { + if (emsha::EMSHAResult::OK != res) { goto exit; } DumpHexString(hs, dig, emsha::SHA256_HASH_SIZE); if (hs != test.output) { - res = emsha::EMSHA_TEST_FAILURE; + res = emsha::EMSHAResult::TestFailure; goto exit; } memset(dig, 0, emsha::SHA256_HASH_SIZE); @@ -106,7 +106,7 @@ runHMACTest(const struct hmacTest test, const string& label) res = emsha::ComputeHMAC(test.key, test.keylen, (uint8_t *)test.input.c_str(), test.input.size(), dig); - if (emsha::EMSHA_ROK != res) { + if (emsha::EMSHAResult::OK != res) { cerr << "(running single pass function test)\n"; goto exit; } @@ -114,15 +114,15 @@ runHMACTest(const struct hmacTest test, const string& label) DumpHexString(hs, dig, emsha::SHA256_HASH_SIZE); if (hs != test.output) { cerr << "(comparing single pass function output)\n"; - res = emsha::EMSHA_TEST_FAILURE; + res = emsha::EMSHAResult::TestFailure; goto exit; } memset(dig, 0, emsha::SHA256_HASH_SIZE); - res = emsha::EMSHA_ROK; + res = emsha::EMSHAResult::OK; exit: - if (emsha::EMSHA_ROK != res) { + if (emsha::EMSHAResult::OK != res) { cerr << "FAILED: " << label << endl; cerr << "\tinput: " << test.input << endl; cerr << "\twanted: " << test.output << endl; @@ -137,7 +137,7 @@ int runHMACTests(const struct hmacTest *tests, size_t nTests, const string& label) { for (uint32_t i = 0; i < nTests; i++) { - if (emsha::EMSHA_ROK != runHMACTest(*(tests + i), label)) { + if (emsha::EMSHAResult::OK != runHMACTest(*(tests + i), label)) { return -1; } } @@ -146,28 +146,28 @@ runHMACTests(const struct hmacTest *tests, size_t nTests, const string& label) } -emsha::EMSHA_RESULT +emsha::EMSHAResult runHashTest(const struct hashTest& test, const string& label) { - emsha::SHA256 ctx; - emsha::EMSHA_RESULT res; - uint8_t dig[emsha::SHA256_HASH_SIZE]; + emsha::SHA256 ctx; + emsha::EMSHAResult res; + uint8_t dig[emsha::SHA256_HASH_SIZE]; string hs; res = ctx.Update((uint8_t *)test.input.c_str(), test.input.size()); - if (emsha::EMSHA_ROK != res) { + if (emsha::EMSHAResult::OK != res) { goto exit; } for (uint32_t n = 0; n < RESULT_ITERATIONS; n++) { res = ctx.Result(dig); - if (emsha::EMSHA_ROK != res) { + if (emsha::EMSHAResult::OK != res) { goto exit; } DumpHexString(hs, dig, emsha::SHA256_HASH_SIZE); if (hs != test.output) { - res = emsha::EMSHA_TEST_FAILURE; + res = emsha::EMSHAResult::TestFailure; goto exit; } memset(dig, 0, emsha::SHA256_HASH_SIZE); @@ -177,28 +177,28 @@ runHashTest(const struct hashTest& test, const string& label) ctx.Reset(); res = ctx.Update((uint8_t *)test.input.c_str(), test.input.size()); - if (emsha::EMSHA_ROK != res) { + if (emsha::EMSHAResult::OK != res) { goto exit; } for (uint32_t n = 0; n < RESULT_ITERATIONS; n++) { res = ctx.Result(dig); - if (emsha::EMSHA_ROK != res) { + if (emsha::EMSHAResult::OK != res) { goto exit; } DumpHexString(hs, dig, emsha::SHA256_HASH_SIZE); if (hs != test.output) { - res = emsha::EMSHA_TEST_FAILURE; + res = emsha::EMSHAResult::TestFailure; goto exit; } memset(dig, 0, emsha::SHA256_HASH_SIZE); } // Test that the single-pass function works. - res = emsha::sha256Digest((uint8_t *) test.input.c_str(), + res = emsha::SHA256Digest((uint8_t *) test.input.c_str(), test.input.size(), dig); - if (emsha::EMSHA_ROK != res) { + if (emsha::EMSHAResult::OK != res) { cerr << "(running single pass function test)\n"; goto exit; } @@ -206,14 +206,14 @@ runHashTest(const struct hashTest& test, const string& label) DumpHexString(hs, dig, emsha::SHA256_HASH_SIZE); if (hs != test.output) { cerr << "(comparing single pass function output)\n"; - res = emsha::EMSHA_TEST_FAILURE; + res = emsha::EMSHAResult::TestFailure; goto exit; } memset(dig, 0, emsha::SHA256_HASH_SIZE); - res = emsha::EMSHA_ROK; + res = emsha::EMSHAResult::OK; exit: - if (emsha::EMSHA_ROK != res) { + if (emsha::EMSHAResult::OK != res) { cerr << "FAILED: " << label << endl; cerr << "\tinput: '" << test.input << "'" << endl; cerr << "\twanted: " << test.output << endl; @@ -227,7 +227,7 @@ int runHashTests(const struct hashTest *tests, const size_t ntests, const string& label) { for (uint32_t i = 0; i < ntests; i++) { - if (emsha::EMSHA_ROK != runHashTest(*(tests + i), label)) { + if (emsha::EMSHAResult::OK != runHashTest(*(tests + i), label)) { return -1; } } diff --git a/test_utils.h b/test_utils.h index 52a40dc..74783c7 100644 --- a/test_utils.h +++ b/test_utils.h @@ -64,13 +64,13 @@ void dump_pair(std::uint8_t *, std::uint8_t *); // SHA-256 testing functions. -emsha::EMSHA_RESULT runHashTest(const struct hashTest& test, const std::string& label); +emsha::EMSHAResult runHashTest(const struct hashTest& test, const std::string& label); int runHashTests(const struct hashTest *tests, const std::size_t nTests, const std::string& label); // HMAC-SHA-256 testery. -emsha::EMSHA_RESULT runHMACTest(struct hmacTest test, const std::string& label); +emsha::EMSHAResult runHMACTest(struct hmacTest& test, const std::string& label); int runHMACTests(const struct hmacTest *tests, std::size_t nTests, const std::string& label);