Refactor to standard layout.

This commit is contained in:
2023-10-19 20:41:55 -07:00
parent 0d7a91b69c
commit 59e6abff06
15 changed files with 40 additions and 39 deletions

198
include/emsha/emsha.h Normal file
View File

@@ -0,0 +1,198 @@
///
/// \file emsha/emsha.h
/// \author K. Isom <kyle@imap.cc>
/// \date 2015-12-17
/// \brief Declares an interface for an EMbedded Secure HAshing interface.
///
/// The MIT License (MIT)
///
/// Copyright (c) 2015 K. Isom <coder@kyleisom.net>
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// copy of this software and associated documentation files (the "Software"),
/// to deal in the Software without restriction, including without limitation
/// the rights to use, copy, modify, merge, publish, distribute, sublicense,
/// and/or sell copies of the Software, and to permit persons to whom the
/// Software is furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
///
#ifndef EMSHA_EMSHA_H
#define EMSHA_EMSHA_H
#include <cstdint>
// emsha is an EMbedded Secure HAshing interface.
namespace emsha {
#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
/// if statement that will return with retval if the condition
/// isn't met.
#define EMSHA_CHECK(condition, retval) if (!(condition)) { return (retval); }
#else
/// EMSHA_CHECK is used for sanity checks in certain parts of
/// the code. If asserts are turned on, the check is expanded to
/// an assertion that the condition holds. In this case, retval
/// is not used.
#define EMSHA_CHECK(condition, retval) (assert((condition)))
#endif
/// SHA256_HASH_SIZE is the output length of SHA-256 in bytes.
const std::uint32_t SHA256_HASH_SIZE = 32U;
/// \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.
enum class EMSHAResult : std::uint8_t {
/// An unknown fault occurred. This is a serious bug in the
/// program.
Unknown = 0U,
/// All operations have completed successfully so far.
OK = 1U,
/// The self-test failed.
TestFailure = 2U,
/// A null pointer was passed in as a buffer where it shouldn't
/// have been.
NullPointer = 3U,
/// The Hash is in an invalid state.
InvalidState = 4U,
/// The input to SHA256::update is too large.
InputTooLong = 5U,
/// The self tests have been disabled, but a self-test function
/// was called.
SelfTestDisabled = 6U
} ;
/// A Hash is an abstract base class supporting concrete classes
/// that produce digests of data.
class Hash {
public:
virtual ~Hash() = default;
/// \brief Bring the Hash back to its initial state.
///
/// 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
/// this for a given concrete class should be
/// described in that class's documentation, but
/// in general, it has the effect of preserving
/// any initial state while removing any data
/// written to the Hash via the update method.
///
/// \return An ::EMSHAResult describing the status of the
/// operation.
virtual EMSHAResult Reset() = 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;
/// \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.
///
/// \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;
/// \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;
/// \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;
};
/// \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];
/// uint8_t actual[emsha::SHA256_HASH_SIZE];
///
/// // Fill in expected and actual using the Hash operations.
///
/// if (hash_equal(expected, actual)) {
/// proceed();
/// }
/// ```
///
/// \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);
#ifndef EMSHA_NO_HEXSTRING
/// \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`)
/// bytes in length.
/// \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);
#endif // EMSHA_NO_HEXSTRING
} // end of namespace emsha
#endif // EMSHA_EMSHA_H

180
include/emsha/hmac.h Normal file
View File

@@ -0,0 +1,180 @@
///
/// \file emsha/hmac.h
/// \author K. Isom <kyle@imap.cc>
/// \date 2015-12-17
/// \brief Declares an interface for HMAC tagging.
///
/// The MIT License (MIT)
///
/// Copyright (c) 2015 K. Isom <coder@kyleisom.net>
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// copy of this software and associated documentation files (the "Software"),
/// to deal in the Software without restriction, including without limitation
/// the rights to use, copy, modify, merge, publish, distribute, sublicense,
/// and/or sell copies of the Software, and to permit persons to whom the
/// Software is furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
///
#ifndef EMSHA_HMAC_H
#define EMSHA_HMAC_H
#include <cstdint>
#include "emsha.h"
#include "sha256.h"
namespace emsha {
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 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:
/// \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);
/// \brief Clear any data written to the HMAC.
///
/// 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;
/// \brief Write 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 a
/// concern for this library.
///
/// \param message A byte array containing the message
/// to be written.
/// \param messageLength The message length, in bytes.
/// \return An ::EMSHAResult describing the result of the
/// operation.
///
/// - 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;
/// \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;
/// \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;
/// \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();
private:
uint8_t hstate;
SHA256 ctx;
uint8_t k[HMAC_KEY_LENGTH];
uint8_t buf[SHA256_HASH_SIZE];
EMSHAResult reset();
inline EMSHAResult finalResult(uint8_t *d);
};
/// \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
#endif // EMSHA_HMAC_H

96
include/emsha/internal.h Normal file
View File

@@ -0,0 +1,96 @@
///
/// \file emsha/internal.h
/// \author K. Isom <kyle@imap.cc>
/// \date 2015-12-17
/// \brief Declares internal interfaces for the emsha library.
///
/// The MIT License (MIT)
///
/// Copyright (c) 2015 K. Isom <coder@kyleisom.net>
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// copy of this software and associated documentation files (the "Software"),
/// to deal in the Software without restriction, including without limitation
/// the rights to use, copy, modify, merge, publish, distribute, sublicense,
/// and/or sell copies of the Software, and to permit persons to whom the
/// Software is furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
///
#ifndef EMSHA_INTERNAL_H
#define EMSHA_INTERNAL_H
#include <cstdint>
using std::uint8_t;
using std::uint32_t;
namespace emsha {
static inline uint32_t
rotr32(uint32_t x, uint8_t n)
{
return ((x >> n) | (x << (32 - n)));
}
static inline uint32_t
sha_ch(uint32_t x, uint32_t y, uint32_t z)
{
return ((x & y) ^ ((~x) & z));
}
static inline uint32_t
sha_maj(uint32_t x, uint32_t y, uint32_t z)
{
return (x & y) ^ (x & z) ^ (y & z);
}
static inline uint32_t
sha_Sigma0(uint32_t x)
{
return rotr32(x, 2) ^ rotr32(x, 13) ^ rotr32(x, 22);
}
static inline uint32_t
sha_Sigma1(uint32_t x)
{
return rotr32(x, 6) ^ rotr32(x, 11) ^ rotr32(x, 25);
}
static inline uint32_t
sha_sigma0(uint32_t x)
{
return rotr32(x, 7) ^ rotr32(x, 18) ^ (x >> 3);
}
static inline uint32_t
sha_sigma1(uint32_t x)
{
return rotr32(x, 17) ^ rotr32(x, 19) ^ (x >> 10);
}
} // end of namespace emsha
#endif // EMSHA_INTERNAL_H

194
include/emsha/sha256.h Normal file
View File

@@ -0,0 +1,194 @@
///
/// \file emsha/sha256.h
/// \author K. Isom <kyle@imap.cc>
/// \date 2015-12-17
/// \brief Declares an interface for producing SHA-256 hashes.
///
/// The MIT License (MIT)
///
/// Copyright (c) 2015 K. Isom <coder@kyleisom.net>
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// copy of this software and associated documentation files (the "Software"),
/// to deal in the Software without restriction, including without limitation
/// the rights to use, copy, modify, merge, publish, distribute, sublicense,
/// and/or sell copies of the Software, and to permit persons to whom the
/// Software is furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
/// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
/// IN THE SOFTWARE.
///
#ifndef EMSHA_SHA256_H
#define EMSHA_SHA256_H
#include <cstdint>
#include <array>
#include <emsha/emsha.h>
namespace emsha {
/// SHA256_MB_SIZE is the size of a message block.
const uint32_t SHA256_MB_SIZE = 64;
class SHA256 : Hash {
public:
/// \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.
~SHA256();
/// \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;
/// \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;
/// \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;
/// \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;
/// \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:
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.
EMSHAResult hStatus;
uint8_t hComplete;
// mb is the message block, and mbi is the message
// block index.
uint8_t mbi;
std::array<uint8_t, SHA256_MB_SIZE> mb;
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
/// \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
#endif // EMSHA_SHA256_H