From f0245bd648f0408dcb3ad1170764fc4d24b97747 Mon Sep 17 00:00:00 2001 From: BytesGalore Date: Mon, 15 Feb 2016 15:27:37 +0100 Subject: [PATCH] hashes/sha256: add sha256-chain computation and verification functions * also added unittetst for it x[SQUASH ME] separated out all waypoints test and increased waypoints --- sys/hashes/sha256.c | 140 ++++++++++++++++ sys/include/hashes/sha256.h | 79 +++++++++ .../tests-hashes/tests-hashes-sha256-chain.c | 158 ++++++++++++++++++ .../tests-hashes/tests-hashes-sha256.c | 14 ++ tests/unittests/tests-hashes/tests-hashes.c | 1 + tests/unittests/tests-hashes/tests-hashes.h | 7 + 6 files changed, 399 insertions(+) create mode 100644 tests/unittests/tests-hashes/tests-hashes-sha256-chain.c diff --git a/sys/hashes/sha256.c b/sys/hashes/sha256.c index aef002c73a..51d8cab73f 100644 --- a/sys/hashes/sha256.c +++ b/sys/hashes/sha256.c @@ -1,6 +1,7 @@ /*- * Copyright 2005 Colin Percival * Copyright 2013 Christian Mehlis & René Kijewski + * Copyright 2016 Martin Landsmann * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,11 +38,13 @@ * @author Colin Percival * @author Christian Mehlis * @author Rene Kijewski + * @author Martin Landsmann * * @} */ #include +#include #include "hashes/sha256.h" #include "board.h" @@ -318,3 +321,140 @@ const unsigned char *hmac_sha256(const unsigned char *key, return result; } + +/** + * @brief helper to compute sha256 inplace for the given buffer + * + * @param[in, out] element the buffer to compute a sha256 and store it back to it + * + */ +static inline void sha256_inplace(unsigned char element[SHA256_DIGEST_LENGTH]) +{ + sha256_context_t ctx; + sha256_init(&ctx); + sha256_update(&ctx, element, SHA256_DIGEST_LENGTH); + sha256_final(element, &ctx); +} + +unsigned char *sha256_chain(const unsigned char *seed, size_t seed_length, + size_t elements, unsigned char *tail_element) +{ + unsigned char tmp_element[SHA256_DIGEST_LENGTH]; + + /* assert if no sha256-chain can be created */ + assert(elements >= 2); + + /* 1st iteration */ + sha256(seed, seed_length, tmp_element); + + /* perform consecutive iterations minus the first one */ + for (size_t i = 0; i < (elements - 1); ++i) { + sha256_inplace(tmp_element); + } + + /* store the result */ + memcpy(tail_element, tmp_element, SHA256_DIGEST_LENGTH); + + return tail_element; +} + +unsigned char *sha256_chain_with_waypoints(const unsigned char *seed, + size_t seed_length, + size_t elements, + unsigned char *tail_element, + sha256_chain_idx_elm_t *waypoints, + size_t *waypoints_length) +{ + /* assert if no sha256-chain can be created */ + assert(elements >= 2); + + /* assert to prevent division by 0 */ + assert(*waypoints_length > 0); + + /* assert if no waypoints can be created */ + assert(*waypoints_length > 1); + + /* if we have enough space we store the whole chain */ + if (*waypoints_length >= elements) { + /* 1st iteration */ + sha256(seed, seed_length, waypoints[0].element); + waypoints[0].index = 0; + + /* perform consecutive iterations starting at index 1*/ + for (size_t i = 1; i < elements; ++i) { + sha256_context_t ctx; + sha256_init(&ctx); + sha256_update(&ctx, waypoints[(i - 1)].element, SHA256_DIGEST_LENGTH); + sha256_final(waypoints[i].element, &ctx); + waypoints[i].index = i; + } + + /* store the result */ + memcpy(tail_element, waypoints[(elements - 1)].element, SHA256_DIGEST_LENGTH); + *waypoints_length = (elements - 1); + + return tail_element; + } + else { + unsigned char tmp_element[SHA256_DIGEST_LENGTH]; + size_t waypoint_streak = (elements / *waypoints_length); + + /* 1st waypoint iteration */ + sha256(seed, seed_length, tmp_element); + for (size_t i = 1; i < waypoint_streak; ++i) { + sha256_inplace(tmp_element); + } + memcpy(waypoints[0].element, tmp_element, SHA256_DIGEST_LENGTH); + waypoints[0].index = (waypoint_streak - 1); + + /* index of the current computed element in the chain */ + size_t index = (waypoint_streak - 1); + + /* consecutive waypoint iterations */ + size_t j = 1; + for (; j < *waypoints_length; ++j) { + for (size_t i = 0; i < waypoint_streak; ++i) { + sha256_inplace(tmp_element); + index++; + } + memcpy(waypoints[j].element, tmp_element, SHA256_DIGEST_LENGTH); + waypoints[j].index = index; + } + + /* store/pass the last used index in the waypoint array */ + *waypoints_length = (j - 1); + + /* remaining iterations down to elements */ + for (size_t i = index; i < (elements - 1); ++i) { + sha256_inplace(tmp_element); + } + + /* store the result */ + memcpy(tail_element, tmp_element, SHA256_DIGEST_LENGTH); + + return tail_element; + } +} + +int sha256_chain_verify_element(unsigned char *element, + size_t element_index, + unsigned char *tail_element, + size_t chain_length) +{ + unsigned char tmp_element[SHA256_DIGEST_LENGTH]; + + int delta_count = (chain_length - element_index); + + /* assert if we have an index mismatch */ + assert(delta_count >= 1); + + memcpy((void*)tmp_element, (void*)element, SHA256_DIGEST_LENGTH); + + /* perform all consecutive iterations down to tail_element */ + for (int i = 0; i < (delta_count - 1); ++i) { + sha256_inplace(tmp_element); + } + + /* return if the computed element equals the tail_element */ + return (memcmp(tmp_element, tail_element, SHA256_DIGEST_LENGTH) != 0); +} diff --git a/sys/include/hashes/sha256.h b/sys/include/hashes/sha256.h index 7d2d712464..c4eb80ed02 100644 --- a/sys/include/hashes/sha256.h +++ b/sys/include/hashes/sha256.h @@ -1,6 +1,7 @@ /*- * Copyright 2005 Colin Percival * Copyright 2013 Christian Mehlis & René Kijewski + * Copyright 2016 Martin Landsmann * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -68,6 +69,16 @@ typedef struct { unsigned char buf[64]; } sha256_context_t; +/** + * @brief sha256-chain indexed element + */ +typedef struct { + /** the position of this element in its chain */ + size_t index; + /** the element */ + unsigned char element[SHA256_DIGEST_LENGTH]; +} sha256_chain_idx_elm_t; + /** * @brief SHA-256 initialization. Begins a SHA-256 operation. * @@ -124,6 +135,74 @@ const unsigned char *hmac_sha256(const unsigned char *key, size_t message_length, unsigned char *result); +/** + * @brief function to produce a hash chain statring with a given seed element. + * The chain is computed by taking the sha256 from the seed, + * hash the resulting sha256 and continuing taking sha256 + * from each result consecutively. + * + * @param[in] seed the seed of the sha256-chain, i.e. the first element + * @param[in] seed_length the size of seed in bytes + * @param[in] elements the number of chained elements, + * i.e. the index of the last element is (elements-1) + * @param[out] tail_element the final element of the sha256-chain + * + * @returns pointer to tail_element + */ +unsigned char *sha256_chain(const unsigned char *seed, size_t seed_length, + size_t elements, unsigned char *tail_element); + +/** + * @brief function to produce a hash chain statring with a given seed element. + * The chain is computed the same way as done with sha256_chain(). + * Additionally intermediate elements are saved while computing the chain. + * This slows down computation, but provides the caller with indexed + * "waypoint"-elements. + * They are supposed to shortcut computing verification elements, + * this comes in handy when using long chains, + * e.g. a chain with 232 elements. + * + * @param[in] seed the seed of the sha256-chain, i.e. the first element + * @param[in] seed_length the size of seed in bytes + * @param[in] elements the number of chained elements, + * i.e. the index of the last element is (elements-1) + * @param[out] tail_element the final element of the sha256-chain + * @param[out] waypoints intermediate elements are stored there. + * @param[in, out] waypoints_length the size of the waypoints array. + * If the given size is equal or greater elements, the complete + * chain will be stored. + * Otherwise every n-th element is stored where: + * n = floor(elements / waypoints_length); + * floor is implicitly used since we perform unsigned integer division. + * The last used waypoint index is stored in the variable after call. + * That is (elements - 1) if the complete chain is stored, + * and (*waypoints_length - 1) if we only store some waypoints. + * + * @returns pointer to tail_element + */ +unsigned char *sha256_chain_with_waypoints(const unsigned char *seed, + size_t seed_length, + size_t elements, + unsigned char *tail_element, + sha256_chain_idx_elm_t *waypoints, + size_t *waypoints_length); + +/** + * @brief function to verify if a given chain element is part of the chain. + * + * @param[in] element the chain element to be verified + * @param[in] element_index the position in the chain + * @param[in] tail_element the last element of the sha256-chain + * @param[in] chain_length the number of elements in the chain + * + * @returns 0 if element is verified to be part of the chain at element_index + * 1 if the element cannot be verified as part of the chain + */ +int sha256_chain_verify_element(unsigned char *element, + size_t element_index, + unsigned char *tail_element, + size_t chain_length); + #ifdef __cplusplus } #endif diff --git a/tests/unittests/tests-hashes/tests-hashes-sha256-chain.c b/tests/unittests/tests-hashes/tests-hashes-sha256-chain.c new file mode 100644 index 0000000000..98afbb6850 --- /dev/null +++ b/tests/unittests/tests-hashes/tests-hashes-sha256-chain.c @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2016 Martin Landsmann + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + + /** + * @ingroup unittests + * @{ + * + * @file + * @brief testcases for the sha256-chain implementation + * + * @author Martin Landsmann + * + * @} + */ + +#include +#include +#include +#include + +#include "embUnit/embUnit.h" + +#include "hashes/sha256.h" + +#include "tests-hashes.h" + +static void test_sha256_hash_chain(void) +{ + const char strSeed[] = "My cool secret seed, you'll never guess it ;) 12345"; + static unsigned char tail_hash_chain_element[SHA256_DIGEST_LENGTH]; + + /* we produce a sha256-chain of 257 elements */ + size_t elements = 257; + + memset(tail_hash_chain_element, 0, SHA256_DIGEST_LENGTH); + TEST_ASSERT(sha256_chain((unsigned char*)strSeed, strlen(strSeed), + elements, tail_hash_chain_element) != NULL); + + /* we check if the first element is part of the chain */ + unsigned char element_one[SHA256_DIGEST_LENGTH]; + sha256((unsigned char*)strSeed, strlen(strSeed), element_one); + TEST_ASSERT(sha256_chain_verify_element(element_one, 0, + tail_hash_chain_element, elements) == 0); + + /* now we check if the test fails if the index is wrong */ + TEST_ASSERT(sha256_chain_verify_element(element_one, 2, + tail_hash_chain_element, elements) == 1); + + /* now we check if other elements are also part of the chain */ + unsigned char tmp_element[SHA256_DIGEST_LENGTH]; + memcpy((void*)tmp_element, (void*)element_one, SHA256_DIGEST_LENGTH); + + /* since we know the seed we build every element and test if its in the chain */ + for (size_t i = 1; i < elements; ++i) { + sha256_context_t ctx; + sha256_init(&ctx); + sha256_update(&ctx, tmp_element, SHA256_DIGEST_LENGTH); + sha256_final(tmp_element, &ctx); + + TEST_ASSERT(sha256_chain_verify_element(tmp_element, i, + tail_hash_chain_element, elements) == 0); + } +} + +static void test_sha256_hash_chain_with_waypoints(void) +{ + const char strSeed[] = "My cool secret seed, you'll never guess it ;P 123456!"; + static unsigned char tail_hash_chain_element[SHA256_DIGEST_LENGTH]; + + /* we produce a sha256-chain of 257 elements */ + size_t elements = 257; + + /* the first element of the hash chain */ + unsigned char element_one[SHA256_DIGEST_LENGTH]; + sha256((unsigned char*)strSeed, strlen(strSeed), element_one); + + /* now we check storing some waypoints, lets say 10 */ + size_t waypoints_length = 10; + sha256_chain_idx_elm_t waypoints[waypoints_length]; + memset(tail_hash_chain_element, 0, SHA256_DIGEST_LENGTH); + + sha256_chain_with_waypoints((unsigned char*)strSeed, + strlen(strSeed), + elements, + tail_hash_chain_element, + waypoints, + &waypoints_length); + + /* we test if the chain has been computed properly */ + TEST_ASSERT(sha256_chain_verify_element(element_one, 0, + tail_hash_chain_element, elements) == 0); + + /* and we check if our waypoints are properly stored */ + for (size_t i = 0; i < (waypoints_length + 1); ++i) { + TEST_ASSERT(sha256_chain_verify_element(waypoints[i].element, + waypoints[i].index, + tail_hash_chain_element, elements) == 0); + } +} + +static void test_sha256_hash_chain_store_whole(void) +{ + const char strSeed[] = "My cool secret seed, you'll never guess it ;P 123456!"; + static unsigned char tail_hash_chain_element[SHA256_DIGEST_LENGTH]; + + /* now we check storing the whole chain + * not a too large one though to remain inside the stack bounds + */ + size_t elements = 17; + + /* the first element of the hash chain */ + unsigned char element_one[SHA256_DIGEST_LENGTH]; + sha256((unsigned char*)strSeed, strlen(strSeed), element_one); + + size_t whole_chain_length = elements; + sha256_chain_idx_elm_t waypoints_whole_chain[whole_chain_length]; + + memset(tail_hash_chain_element, 0, SHA256_DIGEST_LENGTH); + + sha256_chain_with_waypoints((unsigned char*)strSeed, + strlen(strSeed), + whole_chain_length, + tail_hash_chain_element, + waypoints_whole_chain, + &whole_chain_length); + + /* we test again if the chain has been computed properly */ + TEST_ASSERT(sha256_chain_verify_element(element_one, 0, + tail_hash_chain_element, elements) == 0); + + /* and we check if our complete chain has been properly stored */ + TEST_ASSERT( (whole_chain_length + 1) == elements ); + + for (size_t i = 0; i < (whole_chain_length + 1); ++i) { + TEST_ASSERT(sha256_chain_verify_element(waypoints_whole_chain[i].element, + waypoints_whole_chain[i].index, + tail_hash_chain_element, elements) == 0); + } +} + +Test *tests_hashes_sha256_chain_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_sha256_hash_chain), + new_TestFixture(test_sha256_hash_chain_with_waypoints), + new_TestFixture(test_sha256_hash_chain_store_whole), + }; + + EMB_UNIT_TESTCALLER(hashes_sha256_tests, NULL, NULL, + fixtures); + + return (Test *)&hashes_sha256_tests; +} diff --git a/tests/unittests/tests-hashes/tests-hashes-sha256.c b/tests/unittests/tests-hashes/tests-hashes-sha256.c index cb71d81f11..865562b181 100644 --- a/tests/unittests/tests-hashes/tests-hashes-sha256.c +++ b/tests/unittests/tests-hashes/tests-hashes-sha256.c @@ -8,6 +8,20 @@ * directory for more details. */ + /** + * @ingroup unittests + * @{ + * + * @file + * @brief testcases for the sha256 implementation + * + * @author Christian Mehlis + * @author Philipp Rosenkranz + * @author Martin Landsmann + * + * @} + */ + #include #include #include diff --git a/tests/unittests/tests-hashes/tests-hashes.c b/tests/unittests/tests-hashes/tests-hashes.c index 1e9e79983c..c66911e61a 100644 --- a/tests/unittests/tests-hashes/tests-hashes.c +++ b/tests/unittests/tests-hashes/tests-hashes.c @@ -26,4 +26,5 @@ void tests_hashes(void) TESTS_RUN(tests_hashes_sha1_tests()); TESTS_RUN(tests_hashes_sha256_tests()); TESTS_RUN(tests_hashes_sha256_hmac_tests()); + TESTS_RUN(tests_hashes_sha256_chain_tests()); } diff --git a/tests/unittests/tests-hashes/tests-hashes.h b/tests/unittests/tests-hashes/tests-hashes.h index a533c45fdd..c6dc6a2dc3 100644 --- a/tests/unittests/tests-hashes/tests-hashes.h +++ b/tests/unittests/tests-hashes/tests-hashes.h @@ -58,6 +58,13 @@ Test *tests_hashes_sha256_tests(void); */ Test *tests_hashes_sha256_hmac_tests(void); +/** + * @brief Generates tests for hashes/sha256.h - sha256-chain + * + * @return embUnit tests if successful, NULL if not. + */ +Test *tests_hashes_sha256_chain_tests(void); + #ifdef __cplusplus } #endif