diff --git a/README.md b/README.md index 15897bb..eae690c 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,35 @@ -# [hmac_sha256](https://github.com/h5p9sl/hmac_sha256) -*A SHA256 HMAC implementation in C/C++* +# hmac_sha256 +*Minimal HMAC-SHA256 implementation in C / C++* + +This repository provides minimal HMAC-Sha256 code you can copy into your own projects. +The `hmac_sha256` function looks like this: +```cpp +size_t // Returns the number of bytes written to `out` +hmac_sha256( + // [in]: The key and its length. + // Should be at least 32 bytes long for optimal security. + const void* key, const size_t keylen, + + // [in]: The data to hash alongside the key. + const void* data, const size_t datalen, + + // [out]: The output hash. + // Should be 32 bytes long. If it's less than 32 bytes, + // the resulting hash will be truncated to the specified length. + void* out, const size_t outlen +); +``` + +## Contributing +All contributions are welcome, feature requests, or issues. +I aim to tailor this code not only for myself, but for other's use cases too. ## Usage Example (C++) +https://github.com/h5p9sl/hmac_sha256/blob/79a57d2a85aaab32449e5179a4f08f37e38cdee5/examples/simple_example.cpp#L13-L26 + ```cpp -#include "../hmac_sha256.h" - -#include -#include -#include -#include -#include -#include - -#define SHA256_HASH_SIZE 32 - -int main() { const std::string str_data = "Hello World!"; const std::string str_key = "super-secret-key"; - std::stringstream ss_result; // Allocate memory for the HMAC std::vector out(SHA256_HASH_SIZE); @@ -28,23 +40,7 @@ int main() { str_data.data(), str_data.size(), out.data(), out.size() ); - - // Convert `out` to string with std::hex - for (uint8_t x : out) { - ss_result << std::hex << std::setfill('0') << std::setw(2) << (int)x; - } - - // Print out the result - std::cout << "Message: " << str_data << std::endl; - std::cout << "HMAC: " << ss_result.str() << std::endl; - - // This assertion fails if something went wrong - assert( - ss_result.str() == - "4b393abced1c497f8048860ba1ede46a23f1ff5209b18e9c428bddfbb690aad8" - ); - return 0; -} ``` - -Thank you to the [WjCryptLib](https://github.com/WaterJuice/WjCryptLib) for providing the Sha256 implementation! +## Sha256 Implementation +Big thank you to [WjCryptLib](https://github.com/WaterJuice/WjCryptLib) for providing the Sha256 implementation of which this project is based off. +If you need more public domain cryptographic functions in C (sha, aes, md5), check them out. diff --git a/examples/simple_example.cpp b/examples/simple_example.cpp index 231e5e8..71809c3 100644 --- a/examples/simple_example.cpp +++ b/examples/simple_example.cpp @@ -31,6 +31,7 @@ int main() { // Print out the result std::cout << "Message: " << str_data << std::endl; + std::cout << "Key: " << str_key << std::endl; std::cout << "HMAC: " << ss_result.str() << std::endl; // This assertion fails if something went wrong diff --git a/examples/tests.cpp b/examples/tests.cpp index 88f669c..272605e 100644 --- a/examples/tests.cpp +++ b/examples/tests.cpp @@ -12,22 +12,29 @@ typedef std::vector> TestData_t; -void do_tests(const TestData_t& test_vectors) { - // Perform tests +static void verify_result(const std::vector& result, const std::string& expected) { + std::stringstream ss; + for (uint8_t i : result) { ss << std::hex << std::setfill('0') << std::setw(2) << (int)i; } + if (expected != ss.str()) { + std::cout << "*** TEST FAILED ***: \n\t" << ss.str() << " != \n\t" << expected << std::endl; + } else { + std::cout << "Test successful" << std::endl; + } +} + +static void do_tests(const TestData_t& test_vectors) { for (auto tvec : test_vectors) { - std::stringstream ss_result; - std::vector out(SHA256_HASH_SIZE); + std::vector out( + std::get<2>(tvec).size() / 2 + ); + hmac_sha256( std::get<0>(tvec).data(), std::get<0>(tvec).size(), std::get<1>(tvec).data(), std::get<1>(tvec).size(), out.data(), out.size() ); - for (uint8_t i : out) { ss_result << std::hex << std::setfill('0') << std::setw(2) << (int)i; } - if (std::get<2>(tvec) != ss_result.str()) { - std::cout << "TEST FAILED: \n\t" << ss_result.str() << " != \n\t" << std::get<2>(tvec) << std::endl; - } else { - std::cout << "Test successful" << std::endl; - } + + verify_result(out, std::get<2>(tvec)); } } @@ -37,21 +44,43 @@ int main() { // Key Data HMAC { "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", - "Hi There", + "\x48\x69\x20\x54\x68\x65\x72\x65", "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7", }, { - "Jefe", - "what do ya want for nothing?", + /* Test with a key shorter than the length of the HMAC output. */ + "\x4a\x65\x66\x65", + "\x77\x68\x61\x74\x20\x64\x6f\x20\x79\x61\x20\x77\x61\x6e\x74\x20\x66\x6f\x72\x20\x6e\x6f\x74\x68\x69\x6e\x67\x3f", "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843", }, { + /* Test with a combined length of key and data that is larger than 64 + bytes (= block-size of SHA-224 and SHA-256). */ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe", }, { + /* Test with a combined length of key and data that is larger than 64 + bytes (= block-size of SHA-224 and SHA-256). */ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19", "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b", - }, + }, { + /* Test with a truncation of output to 128 bits. */ + "\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c", + "\x54\x65\x73\x74\x20\x57\x69\x74\x68\x20\x54\x72\x75\x6e\x63\x61\x74\x69\x6f\x6e", + "a3b6167473100ee06e0c796c2955552b", + }, { + /* Test with a key larger than 128 bytes (= block-size of SHA-384 and + SHA-512). */ + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + "\x54\x65\x73\x74\x20\x55\x73\x69\x6e\x67\x20\x4c\x61\x72\x67\x65\x72\x20\x54\x68\x61\x6e\x20\x42\x6c\x6f\x63\x6b\x2d\x53\x69\x7a\x65\x20\x4b\x65\x79\x20\x2d\x20\x48\x61\x73\x68\x20\x4b\x65\x79\x20\x46\x69\x72\x73\x74", + "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54", + }, { + /* Test with a key and data that is larger than 128 bytes (= block-size + of SHA-384 and SHA-512). */ + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + "\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x74\x65\x73\x74\x20\x75\x73\x69\x6e\x67\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x6b\x65\x79\x20\x61\x6e\x64\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x64\x61\x74\x61\x2e\x20\x54\x68\x65\x20\x6b\x65\x79\x20\x6e\x65\x65\x64\x73\x20\x74\x6f\x20\x62\x65\x20\x68\x61\x73\x68\x65\x64\x20\x62\x65\x66\x6f\x72\x65\x20\x62\x65\x69\x6e\x67\x20\x75\x73\x65\x64\x20\x62\x79\x20\x74\x68\x65\x20\x48\x4d\x41\x43\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x2e", + "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2", + } }; do_tests(test_vectors); return 0; diff --git a/hmac_sha256.c b/hmac_sha256.c index 568e9e7..ab246ec 100644 --- a/hmac_sha256.c +++ b/hmac_sha256.c @@ -9,76 +9,82 @@ #include #include -#define SIZEOFARRAY(x) sizeof(x) / sizeof(x[0]) +#define SIZEOFARRAY(x) (sizeof(x) / sizeof(x[0])) #define SHA256_BLOCK_SIZE 64 /* LOCAL FUNCTIONS */ -// wrapper for sha256 digest functions -void sha256(const void *data, const unsigned datalen, void *out); -// concatonate src & dest then sha2 digest them -void concat_and_hash(const void *dest, const unsigned destlen, const void *src, - const unsigned srclen, void *out, const unsigned outlen); +// Concatenate X & Y, return hash. +static void* H(const void *x, const size_t xlen, + const void *y, const size_t ylen, + void *out, const size_t outlen +); +// Wrapper for sha256 +static void* sha256(const void *data, const size_t datalen, + void *out, const size_t outlen +); // Declared in hmac_sha256.h -void hmac_sha256(const void *key, const unsigned keylen, const void *data, - const unsigned datalen, void *out, const unsigned outlen) { - uint8_t k[SHA256_BLOCK_SIZE]; // block-sized key derived from 'key' parameter +size_t hmac_sha256(const void *key, const size_t keylen, + const void *data, const size_t datalen, + void *out, const size_t outlen) { + uint8_t k[SHA256_BLOCK_SIZE]; uint8_t k_ipad[SHA256_BLOCK_SIZE]; uint8_t k_opad[SHA256_BLOCK_SIZE]; - uint8_t hash0[SHA256_HASH_SIZE]; - uint8_t hash1[SHA256_HASH_SIZE]; + uint8_t ihash[SHA256_HASH_SIZE]; + uint8_t ohash[SHA256_HASH_SIZE]; + size_t sz; int i; - // Fill 'k' with zero bytes memset(k, 0, SIZEOFARRAY(k)); + memset(k_ipad, 0x36, SHA256_BLOCK_SIZE); + memset(k_opad, 0x5c, SHA256_BLOCK_SIZE); + if (keylen > SHA256_BLOCK_SIZE) { - // If the key is larger than the hash algorithm's block size, we must - // digest it first. - sha256(key, keylen, k); + // If the key is larger than the hash algorithm's + // block size, we must digest it first. + sha256(key, keylen, k, SIZEOFARRAY(k)); } else { memcpy(k, key, keylen); } - // Create outer & inner padded keys - memset(k_ipad, 0x36, SHA256_BLOCK_SIZE); - memset(k_opad, 0x5c, SHA256_BLOCK_SIZE); for (i = 0; i < SHA256_BLOCK_SIZE; i++) { k_ipad[i] ^= k[i]; k_opad[i] ^= k[i]; } - // Perform HMAC algorithm H(K XOR opad, H(K XOR ipad, text)) - // https://tools.ietf.org/html/rfc2104 - concat_and_hash(k_ipad, SIZEOFARRAY(k_ipad), data, datalen, hash0, - SIZEOFARRAY(hash0)); - concat_and_hash(k_opad, SIZEOFARRAY(k_opad), hash0, SIZEOFARRAY(hash0), hash1, - SIZEOFARRAY(hash1)); + // Perform HMAC algorithm: ( https://tools.ietf.org/html/rfc2104 ) + // `H(K XOR opad, H(K XOR ipad, data))` + H(k_ipad, SIZEOFARRAY(k_ipad), + data, datalen, + ihash, SIZEOFARRAY(ihash) + ); + H(k_opad, SIZEOFARRAY(k_opad), + ihash, SIZEOFARRAY(ihash), + ohash, SIZEOFARRAY(ohash) + ); - // Copy the resulting hash the output buffer - // Trunacate sha256 hash if needed - unsigned sz = (SHA256_HASH_SIZE <= outlen) ? SHA256_HASH_SIZE : outlen; - memcpy(out, hash1, sz); + sz = (outlen > SHA256_HASH_SIZE) ? SHA256_HASH_SIZE : outlen; + memcpy(out, ohash, sz); + return sz; } -void concat_and_hash(const void *dest, const unsigned destlen, const void *src, - const unsigned srclen, void *out, const unsigned outlen) { - uint8_t buf[destlen + srclen]; - uint8_t hash[SHA256_HASH_SIZE]; +static void* H(const void *x, const size_t xlen, + const void *y, const size_t ylen, + void *out, const size_t outlen +) { + const size_t buflen = xlen + ylen; + uint8_t buf[buflen]; - memcpy(buf, dest, destlen); - memcpy(buf + destlen, src, srclen); - - // Hash 'buf' and store into into another buffer - sha256(buf, SIZEOFARRAY(buf), hash); - - // Copy the resulting hash to the output buffer - // Truncate hash if needed - unsigned sz = (SHA256_HASH_SIZE <= outlen) ? SHA256_HASH_SIZE : outlen; - memcpy(out, hash, SHA256_HASH_SIZE); + memcpy(buf, x, xlen); + memcpy(buf + xlen, y, ylen); + return sha256(buf, buflen * sizeof(uint8_t), out, outlen); } -void sha256(const void *data, const unsigned datalen, void *out) { +static void* sha256(const void *data, const size_t datalen, + void *out, const size_t outlen +) { + size_t sz; Sha256Context ctx; SHA256_HASH hash; @@ -86,5 +92,7 @@ void sha256(const void *data, const unsigned datalen, void *out) { Sha256Update(&ctx, data, datalen); Sha256Finalise(&ctx, &hash); - memcpy(out, hash.bytes, SHA256_HASH_SIZE); + sz = (outlen > SHA256_HASH_SIZE) ? SHA256_HASH_SIZE : outlen; + return memcpy(out, hash.bytes, sz); } + diff --git a/hmac_sha256.h b/hmac_sha256.h index 1c1e5f3..df6add8 100644 --- a/hmac_sha256.h +++ b/hmac_sha256.h @@ -10,15 +10,21 @@ extern "C" { #endif // __cplusplus -#include +#include -void hmac_sha256( - // [in]: The key and it's length. Should be at least 32 bytes long for optimal security. - const void* key, const unsigned keylen, - // [in]: The data to hash along with the key. - const void* data, const unsigned datalen, - // [out]: The output hash. Should be 32 bytes long, but if it's less than 32 bytes, the function will truncate the resulting hash. - void* out, const unsigned outlen +size_t // Returns the number of bytes written to `out` +hmac_sha256( + // [in]: The key and its length. + // Should be at least 32 bytes long for optimal security. + const void* key, const size_t keylen, + + // [in]: The data to hash alongside the key. + const void* data, const size_t datalen, + + // [out]: The output hash. + // Should be 32 bytes long. If it's less than 32 bytes, + // the resulting hash will be truncated to the specified length. + void* out, const size_t outlen ); #ifdef __cplusplus