Cut a release.
This commit is contained in:
parent
ad07da5a39
commit
4a2c18751a
|
@ -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,
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="ClangTidy" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="Misra" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<scope name="ProjectSources" level="WARNING" enabled="false" />
|
||||
<inspection_tool class="Misra" enabled="true" level="WARNING" enabled_by_default="false">
|
||||
<scope name="ProjectSources" level="WARNING" enabled="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
8
emsha.cc
8
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
|
||||
|
|
128
emsha/emsha.h
128
emsha/emsha.h
|
@ -35,11 +35,10 @@
|
|||
#include <cstdint>
|
||||
|
||||
|
||||
// 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
|
||||
|
||||
|
||||
|
|
218
emsha/hmac.h
218
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
|
||||
|
||||
|
||||
|
|
270
emsha/sha256.h
270
emsha/sha256.h
|
@ -34,184 +34,160 @@
|
|||
#include <cstdint>
|
||||
|
||||
#include <emsha/emsha.h>
|
||||
#include <array>
|
||||
|
||||
|
||||
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<uint8_t, SHA256_MB_SIZE> 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
|
||||
|
||||
|
||||
|
|
96
hmac.cc
96
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;
|
||||
|
|
194
sha256.cc
194
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<uint32_t>(0); i < SHA256_MB_SIZE; i++) {
|
||||
this->mb[i] = static_cast<uint8_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint8_t>(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);
|